@carbonx3/cx3.js 0.1.0 → 0.2.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,138 +1,278 @@
1
- # @carbonx3/cx3.js — CarbonX3 Green Chain SDK
1
+ # @carbonx3/cx3.js
2
2
 
3
- Official JavaScript client (**CX3js**) for **CarbonX3 Green Chain**.
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
- | `@carbonx3/cx3.js` | Role | Green Chain API |
8
- |----------|------|-----------------|
9
- | `Cx3Provider` | HTTP client | `CX3_NODE_URL` + `/api/v1` |
10
- | `Cx3Wallet` | secp256k1 signing | Deploy / send digests |
11
- | `Contract` / `provider.getContract` | ABI-bound calls | `/contract/call` + `/contract/send` |
12
- | `Cx3TokenFactory` | Standard deploys | `POST /deployContract` (CX30–CX3192, CX337–CX341) |
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.
13
10
 
14
- ## Install
11
+ > **Network status:** CarbonX3 Green Chain currently runs as a development network (devnet, chain id `cx3-dev`). Devnet CX3, tokens and contracts are test data with no monetary value and may be cleared when the devnet is reset.
12
+
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
15
29
 
16
30
  ```bash
17
- # when published
18
31
  npm install @carbonx3/cx3.js
32
+ ```
19
33
 
20
- # from monorepo root
21
- npm install ./CX3Web3JS
34
+ ## Create an application
22
35
 
23
- # or inside another package
24
- npm install file:../CX3Web3JS
25
- ```
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) |
26
45
 
27
- Node.js **18+** (native `fetch`).
46
+ A wallet can hold up to five applications. Revoking an application on the same page stops its access tokens within a minute.
28
47
 
29
48
  ## Quick start
30
49
 
31
50
  ```javascript
32
- const {
33
- Cx3Provider,
34
- Cx3Wallet,
35
- Cx3TokenFactory,
36
- readCallValue
37
- } = require("@carbonx3/cx3.js");
38
-
39
- const provider = new Cx3Provider(process.env.CX3_NODE_URL, {
40
- auth: {
41
- user: process.env.CX3_INTERNAL_AUTH_USER,
42
- pass: process.env.CX3_INTERNAL_AUTH_PASS
43
- }
44
- // or: apiKey: process.env.CX3_API_KEY // empty = public (any wallet); cx3wk_ = that wallet only
45
- });
51
+ const { Cx3Provider, Cx3Wallet } = require("@carbonx3/cx3.js");
46
52
 
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
+ });
47
57
  const wallet = new Cx3Wallet(process.env.CX3_PRIVATE_KEY, provider);
48
- await wallet.bindApiKey();
49
58
 
50
- // Read
51
- const token = await provider.getContract(process.env.CONTRACT_ADDRESS, null, wallet);
52
- const balance = await token.balanceOf(wallet.address);
53
- console.log("balance", balance);
59
+ async function main() {
60
+ // Native CX3 balance
61
+ console.log(await provider.getBalance(wallet.address));
54
62
 
55
- const cx3Balance = await provider.getBalance(wallet.address);
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));
56
66
 
57
- // Write
58
- const tx = await token.transfer("0xRecipient…", "1000000000000000000");
59
- await tx.wait();
67
+ // Wallet-signed token transfer (BigInt or decimal string)
68
+ const tx = await token.transfer("0xRecipient…", 10n ** 18n);
69
+ await tx.wait();
60
70
 
61
- await wallet.pauseContract(process.env.CONTRACT_ADDRESS);
62
- await wallet.transferNative("0xRecipient…", 1);
71
+ // Native CX3 transfer
72
+ await wallet.transferNative("0xRecipient…", 1.5);
73
+ }
63
74
 
64
- // Deploy CX30
65
- const factory = new Cx3TokenFactory(wallet);
66
- const deployed = await factory.deployCX30({
67
- name: "My Token",
68
- symbol: "MTK",
69
- initialSupply: "0",
70
- target: "CX3WASM"
75
+ main().catch((err) => {
76
+ console.error(err.code || err.name, err.message);
77
+ process.exitCode = 1;
71
78
  });
72
- console.log(deployed.contractAddress);
73
79
  ```
74
80
 
75
- ## Signing (important)
81
+ ES modules work too: `import { Cx3Provider, Cx3Wallet } from "@carbonx3/cx3.js";`. The remaining examples assume an `async` context.
82
+
83
+ ## Authentication
76
84
 
77
- Digests are **UTF-8 strings**, then `sha256` + **secp256k1** (same as CX3Wallet):
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`.
78
86
 
79
- - Deploy: `deploy|{owner}|{standard}|{name}|{symbol}|{decimals}|{initialSupply}|{timestamp}`
80
- - Send: `{contractAddress}|{method}|{JSON.stringify(params)}|{fromAddress}`
81
- - Owner: `{action}|{address}|{owner}|{timestamp}`
82
- - Native CX3: canonical JSON payload (`buildTransferPayload`) then `wallet.transferNative(to, amount)`
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
+ ```
83
96
 
84
- Address = `0x` + last 20 bytes of `sha256(uncompressedPublicKey)`.
97
+ **Development node.** `new Cx3Provider(devNodeUrl, { devNode: true })` talks to a development node that runs without authentication (its `/api/v1` routes).
85
98
 
86
- ## ABI-bound methods
99
+ **Credentials are required.** Without an application, an API key or `devNode: true`, the constructor throws `CREDENTIALS_REQUIRED`.
87
100
 
88
- Supports:
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`.
89
102
 
90
- 1. Standard interface JSON (`methods.balanceOf.type = "view"`)
91
- 2. Compiler flat ABI (`[{ type: "function", name, inputs, stateMutability }]`)
103
+ ## Reading chain data
92
104
 
93
- Positional args map to ABI input names:
105
+ Public chain data, served by the gateway to every application (a development node answers `DAPP_ONLY`):
94
106
 
95
107
  ```javascript
96
- await token.balanceOf(owner); // → { address: owner } or { account: owner }
97
- await token.transfer(to, amount); // → { to, amount }
98
- await token.call("balanceOf", { account: owner });
108
+ const status = await provider.getStatus(); // { height, hash, timestamp, chainId }
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)
99
115
  ```
100
116
 
101
- ## Environment
117
+ With the `read` scope:
102
118
 
103
- ```bash
104
- CX3_NODE_URL=https://your-node
105
- CX3_INTERNAL_AUTH_USER=cx3-internal
106
- CX3_INTERNAL_AUTH_PASS=…
107
- CX3_API_KEY=cx3wk_… # optional; empty = public (any wallet); set = that wallet only (X-Api-Key)
108
- CX3_PRIVATE_KEY=… # 64-hex or 0x-prefixed
109
- CX3_CHAIN_ID=cx3-dev # native transfer payload (default)
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
128
+
129
+ ```javascript
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();
110
136
  ```
111
137
 
112
- ## Package layout
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");
113
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
114
163
  ```
115
- CX3Web3JS/
116
- index.js # public API
117
- index.d.ts
118
- src/
119
- provider.js
120
- wallet.js
121
- contract.js
122
- factory.js
123
- crypto.js
124
- digest.js
125
- …
126
- test/
164
+
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
127
196
  ```
128
197
 
129
- ## License
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).
199
+
200
+ ## Signing
201
+
202
+ Digests are UTF-8 strings, hashed with SHA-256 and signed with secp256k1, byte-identical to what the Green Chain node verifies:
130
203
 
131
- UNLICENSED — Mersolution Technology Ltd.
204
+ - **Deploy:** `deploy|{owner}|{standard}|{name}|{symbol}|{decimals}|{initialSupply}|{timestamp}`
205
+ - **Contract send:** `{contractAddress}|{method}|{JSON.stringify(params)}|{fromAddress}|{timestamp}`
206
+ - **Owner action:** `{action}|{address}|{owner}|{timestamp}`
207
+ - **Native transfer:** `JSON.stringify({ type, fromAddress, toAddress, amount, nonce, timestamp, data, gasLimit, gasPrice, chainId })`, with 40-hex addresses and no `0x`
132
208
 
133
- ## Docs
209
+ 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. An address is `0x` + the last 20 bytes of `sha256(uncompressedPublicKey)`.
134
210
 
135
- CX3Script documentation site (sidebar **cx3.js SDK**):
211
+ The same building blocks are exported: `buildDeployDigest`, `buildSendDigest`, `buildOwnerActionDigest`, `buildTransferPayload`, `signMessage`, `privateKeyToAddress` and `createPrivateKey`.
212
+
213
+ ## Error handling
214
+
215
+ Every error is a `Cx3Error` with a `code`. `Cx3HttpError` adds the HTTP `status` and the response `body`; `Cx3TxError` adds the transaction `receipt`.
216
+
217
+ ```javascript
218
+ const { Cx3HttpError } = require("@carbonx3/cx3.js");
219
+
220
+ try {
221
+ await token.transfer(to, amount);
222
+ } catch (err) {
223
+ if (err instanceof Cx3HttpError && err.status === 403) console.error("Not allowed:", err.code);
224
+ else throw err;
225
+ }
226
+ ```
227
+
228
+ | Code | Meaning |
229
+ |------|---------|
230
+ | `CREDENTIALS_REQUIRED` | No application, API key or `devNode` was given |
231
+ | `INVALID_CLIENT_CREDENTIALS` | The client ID or secret is not in the `cx3app_…` / `cx3sec_…` format |
232
+ | `invalid_client` | The token endpoint refused the application (wrong secret, or revoked) |
233
+ | `insufficient_scope`, `ROUTE_NOT_ALLOWED` | The route needs another scope, or is not open to applications (403) |
234
+ | `RATE_LIMITED` | The application's rate limit was reached (429) |
235
+ | `INSUFFICIENT_BALANCE`, `GAS_PRICE_TOO_LOW` | The sender cannot pay the gas, or the gas price is below the chain floor |
236
+ | `INVALID_TOKEN_SYMBOL`, `DUPLICATE_TOKEN_SYMBOL` | The symbol is not `A-Z` / `0-9`, or is already in use (409) |
237
+ | `PRIVATE_TOKEN_SENDER`, `PRIVATE_TOKEN_RECIPIENT`, `PRIVATE_TOKEN` | Private token rules (see above) |
238
+ | `TX_FAILED`, `TX_TRACK_TIMEOUT` | The transaction failed, or `tx.wait()` did not see it confirmed in time |
239
+ | `BASIC_AUTH_UNSUPPORTED` | The `auth` option was used |
240
+
241
+ ## Browser applications
242
+
243
+ 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.
244
+
245
+ ## Environment variables
246
+
247
+ | Variable | Used for |
248
+ |----------|----------|
249
+ | `CX3_CLIENT_ID`, `CX3_CLIENT_SECRET` | Application credentials when the options are omitted |
250
+ | `CX3_API_KEY` | Legacy wallet API key |
251
+ | `CX3_NODE_URL` | Gateway URL for `getDefaultProvider()` |
252
+ | `CX3_CHAIN_ID` | Chain id in native transfer signatures (default `cx3-dev`) |
253
+
254
+ ## TypeScript
255
+
256
+ Type declarations (`index.d.ts`) ship with the package.
257
+
258
+ ## Upgrading from 0.1.x
259
+
260
+ CX3js is in beta (0.x), so a minor version may contain breaking changes. Version 0.2.0:
261
+
262
+ - **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.
263
+ - **HTTP Basic was removed.** The `auth` option throws `BASIC_AUTH_UNSUPPORTED`, and `headers` cannot carry `Authorization` or `X-Api-Key`.
264
+ - **Node.js 20.19 or later** is required (`@noble/secp256k1` 3.x).
265
+ - **Fixes:** BigInt params are signed and sent as decimal strings, and `transferNative` sends the gas fields it signs.
266
+ - **New:** public chain reads (`getStatus`, `getTokens`, `getActivity`, `getDexPairs`, `getLpBalance`, `getWit`), private tokens and `isValidTokenSymbol`.
267
+
268
+ ## Documentation
269
+
270
+ - [CX3js SDK reference](https://cx3script.com/docs?doc=cx3js-sdk)
271
+ - [Node.js after deploy](https://cx3script.com/docs?doc=node-after-deploy)
272
+ - [CX3 IIFE for browsers](https://cx3script.com/docs?doc=cx3iife)
273
+ - [CX3Script developer site](https://cx3script.com)
274
+ - [CarbonX3 Green Chain](https://carbonx3.com/)
275
+
276
+ ## License
136
277
 
137
- - Monorepo: `CX3VM-WebSites/cx3docs/docs/cx3js-sdk.md`
138
- - Local: `/CX3VM-WebSites/docs?doc=cx3js-sdk`
278
+ `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,51 @@ 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 from the gateway */
85
+ getStatus(): Promise<{ success: boolean; height: number; hash: string | null; timestamp: number | null; chainId?: string | null }>;
50
86
  getContractInfo(address: string): Promise<any>;
87
+ /** Private token (registry `metadata.visibility === "private"`); kept per address */
88
+ isPrivateToken(address: string): Promise<boolean>;
89
+ /** 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 */
90
+ isTokenAuthorized(token: string, wallet: string): Promise<boolean | null>;
91
+ /** Throws Cx3Error PRIVATE_TOKEN_SENDER / PRIVATE_TOKEN_RECIPIENT for a transfer, transferFrom or mint of a private token between wallets that are not authorized */
92
+ checkPrivateTokenSend(contractAddress: string, method: string, params?: object, from?: string): Promise<void>;
51
93
  getContractState(address: string): Promise<any>;
52
94
  getContractSource(address: string): Promise<any>;
53
95
  getContractBytecode(address: string): Promise<any>;
@@ -61,7 +103,16 @@ export class Cx3Provider {
61
103
  getNonce(address: string): Promise<number>;
62
104
  getPendingNonce(address: string): Promise<any>;
63
105
  bindApiKey(address: string): Promise<any>;
64
- listContracts(opts?: { limit?: number; offset?: number }): Promise<any>;
106
+ /** The gateway lists a private token only when the app's wallet is its owner or an authorized wallet */
107
+ listContracts(opts?: { limit?: number; offset?: number; viewer?: string; owner?: string; standard?: string; type?: string; live?: boolean }): Promise<any>;
108
+ /** Every registry token with this wallet's balance (gateway public read group) */
109
+ getTokens(address: string): Promise<{ success: boolean; address: string; tokens: Cx3WalletToken[]; total: number; unread: number }>;
110
+ /** Wallet history, newest first, without block rewards; next page with `{ before: page.nextBefore }` */
111
+ getActivity(address: string, opts?: { limit?: number; before?: string }): Promise<{ success: boolean; address: string; transactions: any[]; nextBefore: string | null; limit: number }>;
112
+ /** CarbonFi pools */
113
+ getDexPairs(): Promise<{ success: boolean; pairs: any[] }>;
114
+ /** A wallet's LP token balance in one CarbonFi pool (pair id = 40 hex) */
115
+ getLpBalance(pairId: string, address: string): Promise<{ success: boolean; balance: string; totalSupply: string; lpContractAddress: string | null; onChain: boolean }>;
65
116
  getTxStatus(txHash: string): Promise<any>;
66
117
  waitForTransaction(txHash: string, opts?: object): Promise<any>;
67
118
  getContract(address: string, abi?: unknown, signer?: Cx3Wallet | null): Promise<Contract>;
@@ -89,12 +140,14 @@ export class Cx3Wallet {
89
140
  signDeploy(fields: object): Promise<any>;
90
141
  signSend(contractAddress: string, method: string, params?: object): Promise<any>;
91
142
  signOwnerAction(action: string, address: string, owner?: string | null): Promise<any>;
92
- sendTransaction(contractAddress: string, method: string, params?: object, opts?: object): Promise<TransactionResponse>;
143
+ /** A private token's transfer / transferFrom / mint is checked first (PRIVATE_TOKEN_SENDER / _RECIPIENT); `privateCheck: false` skips it */
144
+ sendTransaction(contractAddress: string, method: string, params?: object, opts?: { wait?: number; async?: boolean; privateCheck?: boolean; [k: string]: unknown }): Promise<TransactionResponse>;
145
+ /** 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
146
  deployContract(fields: object): Promise<any>;
94
147
  pauseContract(address: string, owner?: string | null): Promise<any>;
95
148
  unpauseContract(address: string, owner?: string | null): Promise<any>;
96
149
  deleteContract(address: string, owner?: string | null): Promise<any>;
97
- transferNative(to: string, amount: number | string, opts?: object): Promise<TransactionResponse>;
150
+ transferNative(to: string, amount: number | string, opts?: { nonce?: number; timestamp?: number; data?: unknown; gasLimit?: number; gasPrice?: number; chainId?: string; chainIdBind?: boolean; wait?: number }): Promise<TransactionResponse>;
98
151
  bindApiKey(address?: string): Promise<any>;
99
152
  }
100
153
 
@@ -114,13 +167,29 @@ export class Contract {
114
167
  [method: string]: any;
115
168
  }
116
169
 
170
+ export interface Cx3TokenDeployFields {
171
+ name?: string;
172
+ symbol?: string;
173
+ decimals?: number;
174
+ initialSupply?: string | number | bigint;
175
+ target?: "CX3WASM" | "CX3JS";
176
+ sourceCode?: string;
177
+ metadata?: Record<string, string | number | boolean>;
178
+ /** "private": only the owner and authorizedWallets send, receive and see the token (CX30 rendered by the chain; CX31 / CX300 / CX315 / CX3192 need sourceCode) */
179
+ visibility?: "public" | "private";
180
+ /** up to 200 wallets, each added with one signed addToWhitelist after the deploy */
181
+ authorizedWallets?: string[];
182
+ [k: string]: unknown;
183
+ }
184
+
117
185
  export class Cx3TokenFactory {
118
186
  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>;
187
+ /** result.authorized: [{ wallet, success, txHash?, error? }] for a private deploy with authorizedWallets */
188
+ deployCX30(fields?: Cx3TokenDeployFields): Promise<any>;
189
+ deployCX31(fields?: Cx3TokenDeployFields): Promise<any>;
190
+ deployCX300(fields?: Cx3TokenDeployFields): Promise<any>;
191
+ deployCX315(fields?: Cx3TokenDeployFields): Promise<any>;
192
+ deployCX3192(fields?: Cx3TokenDeployFields): Promise<any>;
124
193
  deployCX337(fields?: object): Promise<any>;
125
194
  deployCX338(fields?: object): Promise<any>;
126
195
  deployCX339(fields?: object): Promise<any>;
@@ -144,6 +213,8 @@ export function getDefaultProvider(options?: Cx3ProviderOptions & { url?: string
144
213
  export function readCallValue(response: any): unknown;
145
214
  export function normalizeAddress(addr: string): string;
146
215
  export function walletApiKeyMode(key?: string | null): "public" | "private" | "invalid";
216
+ /** BigInt → decimal string, recursively (params are signed and sent in this form) */
217
+ export function toJsonSafe<T>(value: T): unknown;
147
218
  export function ensure0x(hex: string): string;
148
219
  export function strip0x(hex: string): string;
149
220
  export function buildDeployDigest(fields: object): string;
@@ -155,3 +226,5 @@ export function privateKeyToAddress(privateKey: string | Uint8Array | Buffer): s
155
226
  export function createPrivateKey(): string;
156
227
  export function normalizeAbi(abi: unknown): Record<string, any>;
157
228
  export function argsToParams(inputs: any[], args: any[]): object;
229
+ /** Green Chain token symbol rule: capital letters A-Z and digits 0-9 only. */
230
+ 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,7 +11,8 @@ 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,
@@ -26,10 +27,11 @@ const {
26
27
  } = require("./src/crypto");
27
28
  const { normalizeAbi, argsToParams } = require("./src/abi");
28
29
 
29
- const VERSION = "0.1.0";
30
+ const VERSION = "0.2.0";
30
31
 
31
32
  /**
32
- * Convenience: provider from env `CX3_NODE_URL` (+ optional Basic auth env).
33
+ * Convenience: provider from `options.url` or env `CX3_NODE_URL` — the CX3Api gateway
34
+ * (https://cx3api.carbonx3.com) or a development node that runs without auth.
33
35
  */
34
36
  function getDefaultProvider(options = {}) {
35
37
  const url = options.url || process.env.CX3_NODE_URL;
@@ -55,6 +57,7 @@ module.exports = {
55
57
  ensure0x,
56
58
  strip0x,
57
59
  walletApiKeyMode,
60
+ toJsonSafe,
58
61
  buildDeployDigest,
59
62
  buildSendDigest,
60
63
  buildOwnerActionDigest,
@@ -63,5 +66,6 @@ module.exports = {
63
66
  privateKeyToAddress,
64
67
  createPrivateKey,
65
68
  normalizeAbi,
66
- argsToParams
69
+ argsToParams,
70
+ isValidTokenSymbol
67
71
  };
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@carbonx3/cx3.js",
3
- "version": "0.1.0",
4
- "description": "CarbonX3 Green Chain JavaScript SDK (CX3js) - Provider / Wallet / Contract over REST + JSON",
3
+ "version": "0.2.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",
5
6
  "publishConfig": {
6
7
  "access": "public"
7
8
  },
@@ -23,16 +24,23 @@
23
24
  "green-chain",
24
25
  "cx3js",
25
26
  "cx3wasm",
27
+ "cx3script",
28
+ "blockchain",
29
+ "web3",
30
+ "smart-contracts",
31
+ "tokens",
32
+ "oauth2",
33
+ "secp256k1",
26
34
  "sdk"
27
35
  ],
28
- "author": "Mersolution Technology Ltd.",
36
+ "author": "Mersolution Technology Ltd. (https://cx3script.com)",
29
37
  "license": "UNLICENSED",
30
38
  "type": "commonjs",
31
39
  "engines": {
32
- "node": ">=18"
40
+ "node": ">=20.19.0"
33
41
  },
34
42
  "dependencies": {
35
- "@noble/secp256k1": "^1.7.1"
43
+ "@noble/secp256k1": "^3.2.0"
36
44
  },
37
45
  "exports": {
38
46
  ".": {
package/src/abi.js CHANGED
@@ -99,6 +99,13 @@ function argsToParams(inputs, args) {
99
99
  "BAD_ARGS"
100
100
  );
101
101
  }
102
+ // Built-in chain standards declare `inputs: ["params"]`: one named object
103
+ if (list.length === 1 && list[0] === "params") {
104
+ throw new Cx3Error(
105
+ "This method takes one named params object, e.g. transfer({ to, amount })",
106
+ "BAD_ARGS"
107
+ );
108
+ }
102
109
  if (args.length > list.length) {
103
110
  throw new Cx3Error(
104
111
  `Too many arguments: expected ${list.length}, got ${args.length}`,
@@ -108,7 +115,7 @@ function argsToParams(inputs, args) {
108
115
  const params = {};
109
116
  for (let i = 0; i < args.length; i++) {
110
117
  const input = list[i];
111
- const key = input.name || input.key || `arg${i}`;
118
+ const key = typeof input === "string" ? input : (input.name || input.key || `arg${i}`);
112
119
  params[key] = args[i];
113
120
  }
114
121
  return params;