@carbonx3/cx3.js 0.1.1 → 0.3.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/README.md CHANGED
@@ -1,100 +1,288 @@
1
1
  # @carbonx3/cx3.js
2
2
 
3
- Official **Node.js** client (**CX3js**) for [CarbonX3 Green Chain](https://carbonx3.com/).
3
+ [![npm version](https://img.shields.io/npm/v/@carbonx3/cx3.js.svg)](https://www.npmjs.com/package/@carbonx3/cx3.js)
4
+ [![Node.js version](https://img.shields.io/node/v/@carbonx3/cx3.js.svg)](https://nodejs.org/)
5
+ [![Types](https://img.shields.io/npm/types/@carbonx3/cx3.js.svg)](https://www.npmjs.com/package/@carbonx3/cx3.js)
4
6
 
5
- Talks to the node over **REST + JSON** (`/api/v1/...`) with named method params.
7
+ The official Node.js SDK (**CX3js**) for [CarbonX3 Green Chain](https://carbonx3.com/).
6
8
 
7
- The **browser** client is the **CX3 IIFE**: `<script src="cx3.min.js"></script>` (`window.cx3`). Do not `require` this package in a page.
9
+ CX3js connects server-side applications to CarbonX3 Green Chain through the CX3Api gateway. It signs in as an OAuth2 application, signs transactions locally with secp256k1, binds contract ABIs to plain JavaScript methods and deploys the Green Chain token standards. Requests are REST + JSON with named parameters, not binary calldata.
8
10
 
9
- | Export | Role |
10
- |--------|------|
11
- | `Cx3Provider` | HTTP client (`{node}/api/v1`) |
12
- | `Cx3Wallet` | secp256k1 signing (deploy / send / native CX3) |
13
- | `Contract` / `provider.getContract` | ABI-bound calls |
14
- | `Cx3TokenFactory` | Standard deploys (CX30–CX3192, CX337–CX341) |
11
+ > **Network status:** CarbonX3 Green Chain currently runs as a development network (devnet, chain id `cx3-dev-2`). Devnet CX3, tokens and contracts are test data with no monetary value and may be cleared when the devnet is reset.
15
12
 
16
- ## Install
13
+ ## Features
14
+
15
+ - **OAuth2 application access**: client credentials from CX3WebUI; the access token is requested, cached and renewed for you.
16
+ - **Local signing**: private keys stay in your process, and every write carries the wallet's secp256k1 signature.
17
+ - **ABI-bound contracts**: `token.balanceOf(owner)` and `token.transfer(to, amount)`, with the ABI loaded from the chain.
18
+ - **Token deploys**: CX30, CX31, CX300, CX315 and CX3192, plus the green standards CX337–CX341.
19
+ - **Private tokens**: deploy them, and check authorized wallets before anything is signed.
20
+ - **Public chain reads**: chain status, wallet tokens and activity, CarbonFi pools and CX3WASM interfaces (WIT).
21
+ - **TypeScript declarations included**; works with both `require` and `import`.
22
+
23
+ ## Requirements
24
+
25
+ - Node.js **20.19** or later (native `fetch`).
26
+ - An application (client ID and client secret) created in CX3WebUI, as described below.
27
+
28
+ ## Installation
17
29
 
18
30
  ```bash
19
31
  npm install @carbonx3/cx3.js
20
32
  ```
21
33
 
22
- Node.js **18+** (native `fetch`).
34
+ ## Create an application
35
+
36
+ 1. Open [CX3WebUI](https://cx3webui.carbonx3.com) → **Settings → API → Applications** and connect the wallet the application acts for.
37
+ 2. Name the application, choose its scopes and click **Create App**. The wallet signs the request.
38
+ 3. Store the **client ID** (`cx3app_…`) and the **client secret** (`cx3sec_…`, shown only once) in your server's environment.
39
+
40
+ | Scope | Grants |
41
+ |-------|--------|
42
+ | `read` | Balances, nonces, contracts, ABIs, events, transfers, transactions and read-only calls |
43
+ | `write` | Wallet-signed contract sends and native CX3 transfers |
44
+ | `deploy` | Contract deploys, plus pause, unpause and delete (owner-signed) |
45
+
46
+ A wallet can hold up to five applications. Revoking an application on the same page stops its access tokens within a minute.
23
47
 
24
48
  ## Quick start
25
49
 
26
50
  ```javascript
27
- const { Cx3Provider, Cx3Wallet, Cx3TokenFactory } = require("@carbonx3/cx3.js");
51
+ const { Cx3Provider, Cx3Wallet } = require("@carbonx3/cx3.js");
28
52
 
29
- const provider = new Cx3Provider("https://your-green-chain-node");
53
+ const provider = new Cx3Provider("https://cx3api.carbonx3.com", {
54
+ clientId: process.env.CX3_CLIENT_ID, // cx3app_…
55
+ clientSecret: process.env.CX3_CLIENT_SECRET // cx3sec_…
56
+ });
30
57
  const wallet = new Cx3Wallet(process.env.CX3_PRIVATE_KEY, provider);
31
58
 
32
- const token = await provider.getContract(process.env.CONTRACT_ADDRESS, null, wallet);
33
- const balance = await token.balanceOf(wallet.address);
59
+ async function main() {
60
+ // Native CX3 balance
61
+ console.log(await provider.getBalance(wallet.address));
34
62
 
35
- const tx = await token.transfer("0xRecipient…", "1000000000000000000");
36
- await tx.wait();
63
+ // Token contract; its ABI is loaded from the chain
64
+ const token = await provider.getContract(process.env.CONTRACT_ADDRESS, null, wallet);
65
+ console.log(await token.balanceOf(wallet.address));
66
+
67
+ // Wallet-signed token transfer (BigInt or decimal string)
68
+ const tx = await token.transfer("0xRecipient…", 10n ** 18n);
69
+ await tx.wait();
37
70
 
38
- const factory = new Cx3TokenFactory(wallet);
39
- const deployed = await factory.deployCX30({
40
- name: "My Token",
41
- symbol: "MTK",
42
- initialSupply: "0",
43
- target: "CX3WASM"
71
+ // Native CX3 transfer
72
+ await wallet.transferNative("0xRecipient…", 1.5);
73
+ }
74
+
75
+ main().catch((err) => {
76
+ console.error(err.code || err.name, err.message);
77
+ process.exitCode = 1;
44
78
  });
45
- console.log(deployed.contractAddress);
46
79
  ```
47
80
 
48
- ## Connect
81
+ ES modules work too: `import { Cx3Provider, Cx3Wallet } from "@carbonx3/cx3.js";`. The remaining examples assume an `async` context.
82
+
83
+ ## Authentication
84
+
85
+ **OAuth2 application (recommended).** The provider exchanges `clientId` and `clientSecret` at `POST /oauth/token` (client credentials grant) for a one-hour access token and sends it as `Authorization: Bearer …`. It renews the token a minute before it expires and, if the gateway answers `invalid_token`, fetches a fresh one and retries once. The secret is sent only to the token endpoint. `scope: ["read"]` requests fewer scopes than the application has. When the options are omitted, the provider reads `CX3_CLIENT_ID` and `CX3_CLIENT_SECRET`.
86
+
87
+ The access token identifies the application; every write is still signed by the wallet. A route outside the application's scopes answers `403 insufficient_scope`, and routes reserved for CarbonX3's own services answer `403 ROUTE_NOT_ALLOWED`. Each application has its own rate limit (300 requests per minute by default, `429 RATE_LIMITED`).
88
+
89
+ `provider.bindApiKey(address)` (or `wallet.bindApiKey()`) confirms that a wallet is the one the application belongs to: `{ ok: true }`, otherwise `wallet_mismatch`.
90
+
91
+ **Wallet API key (legacy).** A `cx3wk_…` key from the same settings page still works while applications move to OAuth2. Pass it as `apiKey` (or set `CX3_API_KEY`); it is sent as `X-Api-Key` and carries every scope of its wallet.
92
+
93
+ ```javascript
94
+ const provider = new Cx3Provider("https://cx3api.carbonx3.com", { apiKey: process.env.CX3_API_KEY });
95
+ ```
96
+
97
+ **Development node.** `new Cx3Provider(devNodeUrl, { devNode: true })` talks to a development node that runs without authentication (its `/api/v1` routes).
49
98
 
50
- **1. Green Chain node (usual case)** — pass the node URL only. No username, password, or API key.
99
+ **Credentials are required.** Without an application, an API key or `devNode: true`, the constructor throws `CREDENTIALS_REQUIRED`.
100
+
101
+ **No HTTP Basic.** HTTP Basic is the internal login between Green Chain nodes and CarbonX3 services, not an application credential, so the SDK never sends it: the `auth` option throws `BASIC_AUTH_UNSUPPORTED`, and `headers` cannot carry `Authorization` or `X-Api-Key`.
102
+
103
+ ## Reading chain data
104
+
105
+ Public chain data, served by the gateway to every application (a development node answers `DAPP_ONLY`):
51
106
 
52
107
  ```javascript
53
- const provider = new Cx3Provider("https://your-green-chain-node");
108
+ const status = await provider.getStatus(); // { height, hash, timestamp, chainId, genesisHash }
109
+ const { tokens } = await provider.getTokens(wallet.address); // every token with this wallet's balance
110
+ const page = await provider.getActivity(wallet.address, { limit: 20 }); // newest first, no block rewards
111
+ const older = await provider.getActivity(wallet.address, { before: page.nextBefore });
112
+ const { pairs } = await provider.getDexPairs(); // CarbonFi pools
113
+ const lp = await provider.getLpBalance(pairs[0].id, wallet.address); // LP balance in one pool
114
+ const { wit } = await provider.getWit(contractAddress); // CX3WASM interface (WIT)
54
115
  ```
55
116
 
56
- **2. Your own node** — add HTTP Basic **only if that node requires it**. If it does not, skip this and keep option 1.
117
+ With the `read` scope:
118
+
119
+ | Method | Returns |
120
+ |--------|---------|
121
+ | `getBalance(address)`, `getBalanceAndNonce(address)`, `getNonce(address)`, `getPendingNonce(address)` | Native CX3 balance and nonces |
122
+ | `getContractInfo(address)`, `getAbi(address)`, `listContracts({ limit, offset, owner, standard, type, live })` | Contract registry entries and ABIs |
123
+ | `call(address, method, params)` | A read-only call; the result is in `value` |
124
+ | `getEvents(address, { type, limit, offset })`, `getTransfers(address, { limit, offset })`, `getHolders(address, { limit, offset })` | Contract history |
125
+ | `getAddressTransactions(address, limit)`, `getTxStatus(txHash)` | Transactions |
126
+
127
+ ## Contracts
57
128
 
58
129
  ```javascript
59
- const provider = new Cx3Provider("https://your-node", {
60
- auth: {
61
- user: process.env.CX3_INTERNAL_AUTH_USER,
62
- pass: process.env.CX3_INTERNAL_AUTH_PASS
63
- }
64
- });
130
+ const token = await provider.getContract(address, null, wallet); // ABI from the chain
131
+
132
+ await token.balanceOf(owner); // view → read-only call
133
+ await token.call("balanceOf", { address: owner }); // named parameters (CX30)
134
+ const tx = await token.transfer(to, amount); // transaction → wallet-signed send
135
+ await tx.wait();
136
+ ```
137
+
138
+ Supported ABI shapes:
139
+
140
+ 1. Standard interface JSON: `{ methods: { balanceOf: { type: "view", inputs: [...] } } }`
141
+ 2. Compiler flat ABI: `[{ type: "function", name, inputs, stateMutability }]`
142
+ 3. CX3WASM compiler ABI: `{ functions: [{ name, params, view }] }`
143
+
144
+ `provider.getContract(address)` loads the ABI the chain calls the contract with: shape 2 for contracts compiled to CX3JS, shape 3 for CX3WASM. Built-in chain standards (ABI inputs `["params"]`) take one named object: `await lp.transfer({ to, amount })`. A contract handle created with a provider instead of a wallet can only read; its writes throw `NO_SIGNER`.
145
+
146
+ ## Transactions and fees
147
+
148
+ Every transaction pays gas in CX3 to the validator that produces the block: gas used × gas price (devnet default 0.0001 CX3 per gas).
149
+
150
+ - A native transfer uses 21,000 gas: **2.1 CX3**.
151
+ - A contract send or deploy is admitted only when the sender holds its gas limit × gas price (100,000 gas by default, so at least **10 CX3**) and is charged the gas it used.
152
+
153
+ Too little balance fails with `INSUFFICIENT_BALANCE`, and a gas price below the chain floor with `GAS_PRICE_TOO_LOW`. A send waits for its block by default; with `{ wait: 0 }` it returns at once, and `tx.wait()` polls the transaction status until it is confirmed or failed.
154
+
155
+ ## Deploying tokens
156
+
157
+ ```javascript
158
+ const { Cx3TokenFactory } = require("@carbonx3/cx3.js");
159
+
160
+ const factory = new Cx3TokenFactory(wallet); // the application needs the deploy scope
161
+ const result = await factory.deployCX30({ name: "My Token", symbol: "MTK", initialSupply: "0" });
162
+ console.log(result.contractAddress); // result.contract is a ready Contract handle
65
163
  ```
66
164
 
67
- ### Optional wallet API key
165
+ | Helper | Standard |
166
+ |--------|----------|
167
+ | `deployCX30` | Fungible token |
168
+ | `deployCX31` | Unit / accounting token |
169
+ | `deployCX300` | Real-world asset |
170
+ | `deployCX315` | Loyalty |
171
+ | `deployCX3192` | Soulbound identity |
172
+ | `deployCX337` … `deployCX341` | Green standards: CBAM proof, sustainability report, supply chain, materiality assessment, green claims |
173
+
174
+ The helpers deploy to CX3WASM by default. The chain renders CX30 and CX337–CX341 from the fields; CX31, CX300, CX315 and CX3192 take their CX3Script source as `sourceCode` (otherwise the chain answers `STANDARD_NOT_WASM_DEPLOYABLE`).
175
+
176
+ Token symbols are capital letters `A-Z` and digits `0-9` only. The SDK checks this before signing (`INVALID_TOKEN_SYMBOL`; see `isValidTokenSymbol`), and the chain keeps one contract per symbol (`409 DUPLICATE_TOKEN_SYMBOL`).
177
+
178
+ ## Private tokens
179
+
180
+ A private token is sent, received and seen only by its owner and its authorized wallets. The token contract enforces this on the chain, and the SDK follows the same rules:
181
+
182
+ - **Lists:** `getTokens` shows a private token only to its owner, its authorized wallets and its holders; `listContracts()` shows it only when the application's wallet is its owner or an authorized wallet. Its holders and raw state answer `403 PRIVATE_TOKEN` to every application except the owner's.
183
+ - **Sends:** before a `transfer`, `transferFrom` or `mint` of a private token, the wallet asks the token whether both ends are authorized. If one is not, it throws `PRIVATE_TOKEN_SENDER` or `PRIVATE_TOKEN_RECIPIENT` instead of signing a send the block would refuse. `{ privateCheck: false }` skips the check.
184
+ - **Checks:** `provider.isPrivateToken(token)` and `provider.isTokenAuthorized(token, wallet)`, which returns `true` for the owner or a listed wallet, `false` otherwise and `null` when the node does not answer.
185
+
186
+ ```javascript
187
+ const res = await factory.deployCX30({
188
+ name: "Team Credit",
189
+ symbol: "TEAMC",
190
+ initialSupply: "1000000",
191
+ visibility: "private",
192
+ authorizedWallets: ["0x…", "0x…"] // up to 200; one signed addToWhitelist each after the deploy
193
+ });
194
+ // res.authorized → [{ wallet, success, txHash }]
195
+ await res.contract.addToWhitelist("0xNew…", ""); // later, owner only; removeFromWhitelist removes one
196
+ ```
68
197
 
69
- Leave unset for **public** access (any wallet). Set a `cx3wk_…` key only to lock this client to that wallet. Never put an API key in browser JavaScript.
198
+ A private CX31, CX300, CX315 or CX3192 takes its CX3WebUI source as `sourceCode`; the green standards have no private variant. Private keeps a token out of other wallets' lists and hands, but it does not make it secret: blocks, transfers and contract calls stay readable on the chain, as on [CX3Scan](https://cx3scan.carbonx3.com).
70
199
 
71
200
  ## Signing
72
201
 
73
- Digests are **UTF-8 strings**, then `sha256` + **secp256k1**:
202
+ Digests are UTF-8 strings, hashed with SHA-256 and signed with secp256k1, byte-identical to what the Green Chain node verifies. Each kind starts with its own prefix (signing domains), so a signature of one kind never verifies as another; `chainId` and `genesisHash` are the network's (`provider.getChainIdentity()`, fetched by the wallet methods themselves) and addresses are lower-case `0x` + 40 hex:
203
+
204
+ - **Deploy:** `CX3_DEPLOY_V1|` + `JSON.stringify([chainId, genesisHash, owner, standard, name, symbol, decimals, initialSupply, timestamp])`
205
+ - **Contract send:** `CX3_CALL_V1|` + `JSON.stringify([chainId, genesisHash, contractAddress, method, params, fromAddress, timestamp])`. The chain checks this signature again inside the relayed transaction: a call runs as your wallet only with it.
206
+ - **Owner action:** `CX3_OWNER_V1|` + `JSON.stringify([chainId, genesisHash, action, address, owner, timestamp])`
207
+ - **Site message:** `0x19` + `"CX3 Signed Message:\n"` + UTF-8 length + message (`signPersonalMessage`), the way the CX3Wallet extension and app sign `cx3_sign_message`
208
+ - **Native transfer:** `JSON.stringify({ type, fromAddress, toAddress, amount, nonce, timestamp, data, gasLimit, gasPrice, chainId, genesisHash })`, with 40-hex addresses and no `0x`; `chainId` and `genesisHash` (`0x` + 64 hex) are the network's, from `provider.getChainIdentity()`
74
209
 
75
- - Deploy: `deploy|{owner}|{standard}|{name}|{symbol}|{decimals}|{initialSupply}|{timestamp}`
76
- - Send: `{contractAddress}|{method}|{JSON.stringify(params)}|{fromAddress}`
77
- - Owner: `{action}|{address}|{owner}|{timestamp}`
78
- - Native CX3: `wallet.transferNative(to, amount)`
210
+ BigInt values in params are converted to decimal strings before signing, and exactly those params are sent. Signatures carry a timestamp and are accepted within ±5 minutes of the node's clock; a native transfer is taken only by a block within 10 minutes of its timestamp. The genesis hash ties a native transfer to one network and one genesis: after a chain reset, a transfer signed before it no longer verifies. An address is `0x` + the last 20 bytes of `sha256(uncompressedPublicKey)`.
79
211
 
80
- Address = `0x` + last 20 bytes of `sha256(uncompressedPublicKey)`.
212
+ The same building blocks are exported: `buildDeployDigest`, `buildSendDigest`, `buildOwnerActionDigest` (each takes the identity as its last argument), `buildTransferPayload`, `personalMessageBytes`, `signMessage` (signs a digest as given), `signPersonalMessage`, `privateKeyToAddress` and `createPrivateKey`.
81
213
 
82
- ## ABI-bound methods
214
+ ## Error handling
83
215
 
84
- 1. Standard interface JSON (`methods.balanceOf.type = "view"`)
85
- 2. Compiler flat ABI (`[{ type: "function", name, inputs, stateMutability }]`)
216
+ Every error is a `Cx3Error` with a `code`. `Cx3HttpError` adds the HTTP `status` and the response `body`; `Cx3TxError` adds the transaction `receipt`.
86
217
 
87
218
  ```javascript
88
- await token.balanceOf(owner);
89
- await token.transfer(to, amount);
90
- await token.call("balanceOf", { account: owner });
219
+ const { Cx3HttpError } = require("@carbonx3/cx3.js");
220
+
221
+ try {
222
+ await token.transfer(to, amount);
223
+ } catch (err) {
224
+ if (err instanceof Cx3HttpError && err.status === 403) console.error("Not allowed:", err.code);
225
+ else throw err;
226
+ }
91
227
  ```
92
228
 
93
- ## Docs
229
+ | Code | Meaning |
230
+ |------|---------|
231
+ | `CREDENTIALS_REQUIRED` | No application, API key or `devNode` was given |
232
+ | `INVALID_CLIENT_CREDENTIALS` | The client ID or secret is not in the `cx3app_…` / `cx3sec_…` format |
233
+ | `invalid_client` | The token endpoint refused the application (wrong secret, or revoked) |
234
+ | `insufficient_scope`, `ROUTE_NOT_ALLOWED` | The route needs another scope, or is not open to applications (403) |
235
+ | `RATE_LIMITED` | The application's rate limit was reached (429) |
236
+ | `INSUFFICIENT_BALANCE`, `GAS_PRICE_TOO_LOW` | The sender cannot pay the gas, or the gas price is below the chain floor |
237
+ | `INVALID_TOKEN_SYMBOL`, `DUPLICATE_TOKEN_SYMBOL` | The symbol is not `A-Z` / `0-9`, or is already in use (409) |
238
+ | `PRIVATE_TOKEN_SENDER`, `PRIVATE_TOKEN_RECIPIENT`, `PRIVATE_TOKEN` | Private token rules (see above) |
239
+ | `TX_FAILED`, `TX_TRACK_TIMEOUT` | The transaction failed, or `tx.wait()` did not see it confirmed in time |
240
+ | `BASIC_AUTH_UNSUPPORTED` | The `auth` option was used |
241
+ | `NO_CHAIN_IDENTITY` | The network's chain id or genesis hash is unavailable; a native transfer is not signed without them |
242
+
243
+ ## Browser applications
244
+
245
+ This package is for Node.js servers. Web pages use the **CX3 IIFE** (`cx3.min.js`, `window.cx3`) together with the CX3Wallet extension: [download it](https://cx3script.com/download/cx3.min.js) and see the [CX3 IIFE guide](https://cx3script.com/docs?doc=cx3iife). Keep the application's credentials on your server behind a same-origin proxy, and never put a client secret, an API key or a private key in browser JavaScript.
246
+
247
+ ## Environment variables
248
+
249
+ | Variable | Used for |
250
+ |----------|----------|
251
+ | `CX3_CLIENT_ID`, `CX3_CLIENT_SECRET` | Application credentials when the options are omitted |
252
+ | `CX3_API_KEY` | Legacy wallet API key |
253
+ | `CX3_NODE_URL` | Gateway URL for `getDefaultProvider()` |
254
+
255
+ ## TypeScript
256
+
257
+ Type declarations (`index.d.ts`) ship with the package.
258
+
259
+ ## Upgrading from 0.2.x
260
+
261
+ Version 0.3.0 signs native transfers for the network's new signing format:
262
+
263
+ - **The genesis hash is signed.** `transferNative` asks the network for its chain id and genesis hash (`provider.getChainIdentity()`) unless both are given as `opts.chainId` and `opts.genesisHash`. 0.2.x transfers no longer verify on the chain.
264
+ - **No fallback chain id.** `buildTransferPayload` throws `NO_CHAIN_IDENTITY` without `chainId` and `genesisHash`; the `CX3_CHAIN_ID` variable and the `chainIdBind` option are gone.
265
+ - **Typed digests.** Contract sends, deploys and owner actions sign `CX3_CALL_V1` / `CX3_DEPLOY_V1` / `CX3_OWNER_V1` digests bound to the network (see Signing); the `|`-joined digests of 0.2.x are refused. `signSend`, `signDeploy` and `signOwnerAction` take an optional `{ chainId, genesisHash }`, else they ask the provider (kept for a minute). The `build*Digest` helpers need the identity as their last argument.
266
+ - **Site messages.** `signPersonalMessage` signs the way the wallets sign `cx3_sign_message` (with the `0x19` prefix); `signMessage` still signs a digest as it is.
267
+
268
+ ## Upgrading from 0.1.x
269
+
270
+ CX3js is in beta (0.x), so a minor version may contain breaking changes. Version 0.2.0:
271
+
272
+ - **Credentials are required.** The provider talks to the CX3Api gateway as an OAuth2 application (or with a wallet API key) and throws `CREDENTIALS_REQUIRED` without one. A development node without authentication needs `devNode: true`; 0.1.x used node routes under `/api/v1` by default.
273
+ - **HTTP Basic was removed.** The `auth` option throws `BASIC_AUTH_UNSUPPORTED`, and `headers` cannot carry `Authorization` or `X-Api-Key`.
274
+ - **Node.js 20.19 or later** is required (`@noble/secp256k1` 3.x).
275
+ - **Fixes:** BigInt params are signed and sent as decimal strings, and `transferNative` sends the gas fields it signs.
276
+ - **New:** public chain reads (`getStatus`, `getTokens`, `getActivity`, `getDexPairs`, `getLpBalance`, `getWit`), private tokens and `isValidTokenSymbol`.
277
+
278
+ ## Documentation
94
279
 
95
- - [CX3js SDK](https://cx3script.com/docs?doc=cx3js-sdk)
280
+ - [CX3js SDK reference](https://cx3script.com/docs?doc=cx3js-sdk)
281
+ - [Node.js after deploy](https://cx3script.com/docs?doc=node-after-deploy)
282
+ - [CX3 IIFE for browsers](https://cx3script.com/docs?doc=cx3iife)
283
+ - [CX3Script developer site](https://cx3script.com)
96
284
  - [CarbonX3 Green Chain](https://carbonx3.com/)
97
285
 
98
286
  ## License
99
287
 
100
- UNLICENSED — Mersolution Technology Ltd.
288
+ `UNLICENSED`: proprietary software of Mersolution Technology Ltd. Use is subject to the [CX3Script Terms of Use](https://cx3script.com/terms).
package/index.d.ts CHANGED
@@ -18,15 +18,22 @@ export class Cx3TxError extends Cx3Error {
18
18
  constructor(message: string, code?: string, receipt?: unknown);
19
19
  }
20
20
 
21
- export interface Cx3Auth {
22
- user: string;
23
- pass: string;
24
- }
25
-
26
21
  export interface Cx3ProviderOptions {
27
- apiPrefix?: string;
28
- auth?: Cx3Auth;
22
+ /** OAuth2 client id (cx3app_…) from CX3WebUI → Settings → API → Applications; env CX3_CLIENT_ID */
23
+ clientId?: string;
24
+ /** OAuth2 client secret (cx3sec_…); env CX3_CLIENT_SECRET. Keep it on the server. */
25
+ clientSecret?: string;
26
+ /** ask for fewer scopes than the app has: "read", "write", "deploy" */
27
+ scope?: string | Array<"read" | "write" | "deploy">;
28
+ /** wallet API key (cx3wk_…), until apps move to OAuth2; env CX3_API_KEY */
29
29
  apiKey?: string;
30
+ /** a development node that runs without auth (/api/v1), no credentials */
31
+ devNode?: boolean;
32
+ /** "" on the gateway, "/api/v1" on a development node */
33
+ apiPrefix?: string;
34
+ /** Not supported: throws BASIC_AUTH_UNSUPPORTED (HTTP Basic is the nodes' internal login) */
35
+ auth?: never;
36
+ /** extra headers; Authorization and X-Api-Key are dropped (use clientId / clientSecret or apiKey) */
30
37
  headers?: Record<string, string>;
31
38
  fetch?: typeof fetch;
32
39
  timeoutMs?: number;
@@ -38,16 +45,53 @@ export interface Cx3PageQuery {
38
45
  offset?: number;
39
46
  }
40
47
 
48
+ export interface Cx3WalletToken {
49
+ address: string;
50
+ name?: string;
51
+ symbol?: string;
52
+ decimals?: number;
53
+ standard?: string;
54
+ /** null when the node could not read it */
55
+ balance: string | null;
56
+ totalSupply?: string;
57
+ paused?: boolean;
58
+ /** this wallet deployed it */
59
+ mine: boolean;
60
+ /** private token: listed for its owner, its authorized wallets and holders */
61
+ private?: boolean;
62
+ authorized?: boolean;
63
+ }
64
+
41
65
  export class Cx3Provider {
42
66
  baseUrl: string;
43
67
  apiPrefix: string;
68
+ gateway: boolean;
69
+ devNode: boolean;
70
+ oauth: boolean;
71
+ clientId: string;
44
72
  apiKeyMode: "public" | "private" | "invalid";
73
+ authMode: "oauth" | "apiKey" | "devNode";
74
+ /** Throws CREDENTIALS_REQUIRED without clientId / clientSecret, apiKey or devNode */
45
75
  constructor(baseUrl: string, options?: Cx3ProviderOptions);
76
+ /** The current OAuth2 access token (renewed a minute before it expires); null without an OAuth2 app */
77
+ getAccessToken(force?: boolean): Promise<string | null>;
46
78
  request(path: string, opts?: { method?: string; body?: unknown; query?: Record<string, string | number> }): Promise<any>;
47
79
  call(contractAddress: string, method: string, params?: object, sender?: string): Promise<any>;
48
80
  send(contractAddress: string, method: string, params?: object, extras?: object): Promise<any>;
49
81
  getAbi(address: string): Promise<any>;
82
+ /** WIT interface of a CX3WASM contract */
83
+ getWit(address: string): Promise<{ success: boolean; address: string; target: string | null; wit: string }>;
84
+ /** Chain head, with chainId and genesisHash from the gateway */
85
+ getStatus(): Promise<{ success: boolean; height: number; hash: string | null; timestamp: number | null; chainId?: string | null; genesisHash?: string | null }>;
86
+ /** The network identity a native transfer signs over; throws NO_CHAIN_IDENTITY when unavailable */
87
+ getChainIdentity(): Promise<{ chainId: string; genesisHash: string }>;
50
88
  getContractInfo(address: string): Promise<any>;
89
+ /** Private token (registry `metadata.visibility === "private"`); kept per address */
90
+ isPrivateToken(address: string): Promise<boolean>;
91
+ /** The owner (registry record) or a wallet the token's isWhitelisted view lists; false also for a token without such a list; null = no answer */
92
+ isTokenAuthorized(token: string, wallet: string): Promise<boolean | null>;
93
+ /** Throws Cx3Error PRIVATE_TOKEN_SENDER / PRIVATE_TOKEN_RECIPIENT for a transfer, transferFrom or mint of a private token between wallets that are not authorized */
94
+ checkPrivateTokenSend(contractAddress: string, method: string, params?: object, from?: string): Promise<void>;
51
95
  getContractState(address: string): Promise<any>;
52
96
  getContractSource(address: string): Promise<any>;
53
97
  getContractBytecode(address: string): Promise<any>;
@@ -61,7 +105,16 @@ export class Cx3Provider {
61
105
  getNonce(address: string): Promise<number>;
62
106
  getPendingNonce(address: string): Promise<any>;
63
107
  bindApiKey(address: string): Promise<any>;
64
- listContracts(opts?: { limit?: number; offset?: number }): Promise<any>;
108
+ /** The gateway lists a private token only when the app's wallet is its owner or an authorized wallet */
109
+ listContracts(opts?: { limit?: number; offset?: number; viewer?: string; owner?: string; standard?: string; type?: string; live?: boolean }): Promise<any>;
110
+ /** Every registry token with this wallet's balance (gateway public read group) */
111
+ getTokens(address: string): Promise<{ success: boolean; address: string; tokens: Cx3WalletToken[]; total: number; unread: number }>;
112
+ /** Wallet history, newest first, without block rewards; next page with `{ before: page.nextBefore }` */
113
+ getActivity(address: string, opts?: { limit?: number; before?: string }): Promise<{ success: boolean; address: string; transactions: any[]; nextBefore: string | null; limit: number }>;
114
+ /** CarbonFi pools */
115
+ getDexPairs(): Promise<{ success: boolean; pairs: any[] }>;
116
+ /** A wallet's LP token balance in one CarbonFi pool (pair id = 40 hex) */
117
+ getLpBalance(pairId: string, address: string): Promise<{ success: boolean; balance: string; totalSupply: string; lpContractAddress: string | null; onChain: boolean }>;
65
118
  getTxStatus(txHash: string): Promise<any>;
66
119
  waitForTransaction(txHash: string, opts?: object): Promise<any>;
67
120
  getContract(address: string, abi?: unknown, signer?: Cx3Wallet | null): Promise<Contract>;
@@ -86,15 +139,20 @@ export class Cx3Wallet {
86
139
  derivedAddress: string;
87
140
  hash: string;
88
141
  }>;
89
- signDeploy(fields: object): Promise<any>;
90
- signSend(contractAddress: string, method: string, params?: object): Promise<any>;
91
- signOwnerAction(action: string, address: string, owner?: string | null): Promise<any>;
92
- sendTransaction(contractAddress: string, method: string, params?: object, opts?: object): Promise<TransactionResponse>;
142
+ /** A site message the way the CX3Wallet extension and app sign it (0x19 "CX3 Signed Message:\n" prefix) */
143
+ signPersonalMessage(message: string): Promise<any>;
144
+ /** Typed digests (CX3_DEPLOY_V1 / CX3_CALL_V1 / CX3_OWNER_V1) bound to the network's chainId + genesisHash (from the provider unless given in opts) */
145
+ signDeploy(fields: object, opts?: Cx3ChainIdentity): Promise<any>;
146
+ signSend(contractAddress: string, method: string, params?: object, opts?: Cx3ChainIdentity): Promise<any>;
147
+ signOwnerAction(action: string, address: string, owner?: string | null, opts?: Cx3ChainIdentity): Promise<any>;
148
+ /** A private token's transfer / transferFrom / mint is checked first (PRIVATE_TOKEN_SENDER / _RECIPIENT); `privateCheck: false` skips it */
149
+ sendTransaction(contractAddress: string, method: string, params?: object, opts?: { wait?: number; async?: boolean; privateCheck?: boolean; [k: string]: unknown }): Promise<TransactionResponse>;
150
+ /** Throws Cx3Error INVALID_TOKEN_SYMBOL (before signing) for a symbol outside A-Z / 0-9; the chain answers DUPLICATE_TOKEN_SYMBOL (409) for a symbol in use. */
93
151
  deployContract(fields: object): Promise<any>;
94
152
  pauseContract(address: string, owner?: string | null): Promise<any>;
95
153
  unpauseContract(address: string, owner?: string | null): Promise<any>;
96
154
  deleteContract(address: string, owner?: string | null): Promise<any>;
97
- transferNative(to: string, amount: number | string, opts?: object): Promise<TransactionResponse>;
155
+ transferNative(to: string, amount: number | string, opts?: { nonce?: number; timestamp?: number; data?: unknown; gasLimit?: number; gasPrice?: number; chainId?: string; genesisHash?: string; wait?: number }): Promise<TransactionResponse>;
98
156
  bindApiKey(address?: string): Promise<any>;
99
157
  }
100
158
 
@@ -114,13 +172,29 @@ export class Contract {
114
172
  [method: string]: any;
115
173
  }
116
174
 
175
+ export interface Cx3TokenDeployFields {
176
+ name?: string;
177
+ symbol?: string;
178
+ decimals?: number;
179
+ initialSupply?: string | number | bigint;
180
+ target?: "CX3WASM" | "CX3JS";
181
+ sourceCode?: string;
182
+ metadata?: Record<string, string | number | boolean>;
183
+ /** "private": only the owner and authorizedWallets send, receive and see the token (CX30 rendered by the chain; CX31 / CX300 / CX315 / CX3192 need sourceCode) */
184
+ visibility?: "public" | "private";
185
+ /** up to 200 wallets, each added with one signed addToWhitelist after the deploy */
186
+ authorizedWallets?: string[];
187
+ [k: string]: unknown;
188
+ }
189
+
117
190
  export class Cx3TokenFactory {
118
191
  constructor(wallet: Cx3Wallet);
119
- deployCX30(fields?: object): Promise<any>;
120
- deployCX31(fields?: object): Promise<any>;
121
- deployCX300(fields?: object): Promise<any>;
122
- deployCX315(fields?: object): Promise<any>;
123
- deployCX3192(fields?: object): Promise<any>;
192
+ /** result.authorized: [{ wallet, success, txHash?, error? }] for a private deploy with authorizedWallets */
193
+ deployCX30(fields?: Cx3TokenDeployFields): Promise<any>;
194
+ deployCX31(fields?: Cx3TokenDeployFields): Promise<any>;
195
+ deployCX300(fields?: Cx3TokenDeployFields): Promise<any>;
196
+ deployCX315(fields?: Cx3TokenDeployFields): Promise<any>;
197
+ deployCX3192(fields?: Cx3TokenDeployFields): Promise<any>;
124
198
  deployCX337(fields?: object): Promise<any>;
125
199
  deployCX338(fields?: object): Promise<any>;
126
200
  deployCX339(fields?: object): Promise<any>;
@@ -144,14 +218,27 @@ export function getDefaultProvider(options?: Cx3ProviderOptions & { url?: string
144
218
  export function readCallValue(response: any): unknown;
145
219
  export function normalizeAddress(addr: string): string;
146
220
  export function walletApiKeyMode(key?: string | null): "public" | "private" | "invalid";
221
+ /** BigInt → decimal string, recursively (params are signed and sent in this form) */
222
+ export function toJsonSafe<T>(value: T): unknown;
147
223
  export function ensure0x(hex: string): string;
148
224
  export function strip0x(hex: string): string;
149
- export function buildDeployDigest(fields: object): string;
150
- export function buildSendDigest(contractAddress: string, method: string, params: object, fromAddress: string, timestamp?: number | string): string;
151
- export function buildOwnerActionDigest(action: string, address: string, owner: string, timestamp: number): string;
225
+ /** The network a typed digest or a native transfer is bound to (provider.getChainIdentity()) */
226
+ export interface Cx3ChainIdentity { chainId?: string; genesisHash?: string }
227
+ /** CX3_DEPLOY_V1|[chainId, genesisHash, owner, standard, name, symbol, decimals, initialSupply, timestamp] */
228
+ export function buildDeployDigest(fields: object, identity: Cx3ChainIdentity): string;
229
+ /** CX3_CALL_V1|[chainId, genesisHash, contract, method, params, from, timestamp] */
230
+ export function buildSendDigest(contractAddress: string, method: string, params: object, fromAddress: string, timestamp: number, identity: Cx3ChainIdentity): string;
231
+ /** CX3_OWNER_V1|[chainId, genesisHash, action, address, owner, timestamp] */
232
+ export function buildOwnerActionDigest(action: string, address: string, owner: string, timestamp: number, identity: Cx3ChainIdentity): string;
233
+ /** 0x19 "CX3 Signed Message:\n" + UTF-8 length + message: what a wallet signs for a site message */
234
+ export function personalMessageBytes(message: string): Uint8Array;
235
+ export function signPersonalMessage(message: string, privateKey: string | Uint8Array | Buffer): Promise<any>;
236
+ /** Native TRANSFER payload; fields.chainId and fields.genesisHash are required */
152
237
  export function buildTransferPayload(fields: object): string;
153
238
  export function signMessage(message: string | Uint8Array, privateKey: string | Uint8Array | Buffer): Promise<any>;
154
239
  export function privateKeyToAddress(privateKey: string | Uint8Array | Buffer): string;
155
240
  export function createPrivateKey(): string;
156
241
  export function normalizeAbi(abi: unknown): Record<string, any>;
157
242
  export function argsToParams(inputs: any[], args: any[]): object;
243
+ /** Green Chain token symbol rule: capital letters A-Z and digits 0-9 only. */
244
+ export function isValidTokenSymbol(symbol: unknown): boolean;
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  const { Cx3Provider } = require("./src/provider");
4
- const { Cx3Wallet } = require("./src/wallet");
4
+ const { Cx3Wallet, isValidTokenSymbol } = require("./src/wallet");
5
5
  const { Contract } = require("./src/contract");
6
6
  const { Cx3TokenFactory } = require("./src/factory");
7
7
  const { TransactionResponse } = require("./src/transaction");
@@ -11,25 +11,29 @@ const {
11
11
  normalizeAddress,
12
12
  ensure0x,
13
13
  strip0x,
14
- walletApiKeyMode
14
+ walletApiKeyMode,
15
+ toJsonSafe
15
16
  } = require("./src/utils");
16
17
  const {
17
18
  buildDeployDigest,
18
19
  buildSendDigest,
19
20
  buildOwnerActionDigest,
20
- buildTransferPayload
21
+ buildTransferPayload,
22
+ personalMessageBytes
21
23
  } = require("./src/digest");
22
24
  const {
23
25
  signMessage,
26
+ signPersonalMessage,
24
27
  privateKeyToAddress,
25
28
  createPrivateKey
26
29
  } = require("./src/crypto");
27
30
  const { normalizeAbi, argsToParams } = require("./src/abi");
28
31
 
29
- const VERSION = "0.1.1";
32
+ const VERSION = "0.3.0";
30
33
 
31
34
  /**
32
- * Convenience: provider from env `CX3_NODE_URL` (+ optional Basic auth env).
35
+ * Convenience: provider from `options.url` or env `CX3_NODE_URL` — the CX3Api gateway
36
+ * (https://cx3api.carbonx3.com) or a development node that runs without auth.
33
37
  */
34
38
  function getDefaultProvider(options = {}) {
35
39
  const url = options.url || process.env.CX3_NODE_URL;
@@ -55,13 +59,17 @@ module.exports = {
55
59
  ensure0x,
56
60
  strip0x,
57
61
  walletApiKeyMode,
62
+ toJsonSafe,
58
63
  buildDeployDigest,
59
64
  buildSendDigest,
60
65
  buildOwnerActionDigest,
61
66
  buildTransferPayload,
67
+ personalMessageBytes,
62
68
  signMessage,
69
+ signPersonalMessage,
63
70
  privateKeyToAddress,
64
71
  createPrivateKey,
65
72
  normalizeAbi,
66
- argsToParams
73
+ argsToParams,
74
+ isValidTokenSymbol
67
75
  };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@carbonx3/cx3.js",
3
- "version": "0.1.1",
4
- "description": "CarbonX3 Green Chain JavaScript SDK (CX3js) - Provider / Wallet / Contract over REST + JSON",
5
- "homepage": "https://cx3script.com/docs?doc=cx3js-sdk",
3
+ "version": "0.3.0",
4
+ "description": "Official Node.js SDK (CX3js) for CarbonX3 Green Chain: OAuth2 app access, local secp256k1 signing, ABI-bound contracts and token deploys over REST + JSON",
5
+ "homepage": "https://cx3script.com",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -24,16 +24,23 @@
24
24
  "green-chain",
25
25
  "cx3js",
26
26
  "cx3wasm",
27
+ "cx3script",
28
+ "blockchain",
29
+ "web3",
30
+ "smart-contracts",
31
+ "tokens",
32
+ "oauth2",
33
+ "secp256k1",
27
34
  "sdk"
28
35
  ],
29
- "author": "Mersolution Technology Ltd.",
36
+ "author": "Mersolution Technology Ltd. (https://cx3script.com)",
30
37
  "license": "UNLICENSED",
31
38
  "type": "commonjs",
32
39
  "engines": {
33
- "node": ">=18"
40
+ "node": ">=20.19.0"
34
41
  },
35
42
  "dependencies": {
36
- "@noble/secp256k1": "^1.7.1"
43
+ "@noble/secp256k1": "^3.2.0"
37
44
  },
38
45
  "exports": {
39
46
  ".": {