@decaf-ts/for-fabric 0.12.7 → 0.12.8
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 +177 -146
- package/dist/for-fabric.cjs +1 -1
- package/dist/for-fabric.cjs.map +1 -1
- package/dist/for-fabric.js +1 -1
- package/dist/for-fabric.js.map +1 -1
- package/lib/cjs/cli-module.cjs +105 -1
- package/lib/cjs/cli-module.cjs.map +1 -1
- package/lib/cjs/client/indexes/generation.cjs +7 -2
- package/lib/cjs/client/indexes/generation.cjs.map +1 -1
- package/lib/cjs/contract/OtherLeafletContract.cjs +72 -0
- package/lib/cjs/contract/OtherLeafletContract.cjs.map +1 -0
- package/lib/cjs/contract/OtherLeafletFileContract.cjs +76 -0
- package/lib/cjs/contract/OtherLeafletFileContract.cjs.map +1 -0
- package/lib/cjs/contract/index.cjs +17 -18
- package/lib/cjs/contract/index.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherAudit.cjs +1 -1
- package/lib/cjs/contract/models/OtherAudit.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherBatchShared.cjs +135 -26
- package/lib/cjs/contract/models/OtherBatchShared.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherLeaflet.cjs +1 -1
- package/lib/cjs/contract/models/OtherLeaflet.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherLeafletFile.cjs +1 -1
- package/lib/cjs/contract/models/OtherLeafletFile.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherMarket.cjs +1 -1
- package/lib/cjs/contract/models/OtherMarket.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherProduct.cjs +1 -1
- package/lib/cjs/contract/models/OtherProduct.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherProductImage.cjs +1 -1
- package/lib/cjs/contract/models/OtherProductImage.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherProductShared.cjs +1 -1
- package/lib/cjs/contract/models/OtherProductShared.cjs.map +1 -1
- package/lib/cjs/contract/models/OtherProductStrength.cjs +1 -3
- package/lib/cjs/contract/models/OtherProductStrength.cjs.map +1 -1
- package/lib/cjs/contract/trackedModels/History.cjs +81 -0
- package/lib/cjs/contract/trackedModels/History.cjs.map +1 -0
- package/lib/cjs/contract/trackedModels/OtherAudit.cjs +124 -0
- package/lib/cjs/contract/trackedModels/OtherAudit.cjs.map +1 -0
- package/lib/cjs/contract/trackedModels/OtherLeaflet.cjs +121 -0
- package/lib/cjs/contract/trackedModels/OtherLeaflet.cjs.map +1 -0
- package/lib/cjs/contract/trackedModels/OtherLeafletFile.cjs +71 -0
- package/lib/cjs/contract/trackedModels/OtherLeafletFile.cjs.map +1 -0
- package/lib/cjs/contract/trackedModels/OtherMarket.cjs +90 -0
- package/lib/cjs/contract/trackedModels/OtherMarket.cjs.map +1 -0
- package/lib/cjs/contract/trackedModels/OtherProduct.cjs +83 -0
- package/lib/cjs/contract/trackedModels/OtherProduct.cjs.map +1 -0
- package/lib/cjs/contract/trackedModels/OtherProductImage.cjs +65 -0
- package/lib/cjs/contract/trackedModels/OtherProductImage.cjs.map +1 -0
- package/lib/cjs/contract/trackedModels/OtherProductShared.cjs +105 -0
- package/lib/cjs/contract/trackedModels/OtherProductShared.cjs.map +1 -0
- package/lib/cjs/contract/trackedModels/OtherProductStrength.cjs +91 -0
- package/lib/cjs/contract/trackedModels/OtherProductStrength.cjs.map +1 -0
- package/lib/cjs/contracts/fabric-overrides.cjs.map +1 -1
- package/lib/cjs/version.cjs +1 -1
- package/lib/esm/cli-module.js +105 -1
- package/lib/esm/cli-module.js.map +1 -1
- package/lib/esm/client/fabric-hsm.js.map +1 -1
- package/lib/esm/client/indexes/generation.js +7 -2
- package/lib/esm/client/indexes/generation.js.map +1 -1
- package/lib/esm/contract/OtherLeafletContract.js +63 -0
- package/lib/esm/contract/OtherLeafletContract.js.map +1 -0
- package/lib/esm/contract/OtherLeafletFileContract.js +67 -0
- package/lib/esm/contract/OtherLeafletFileContract.js.map +1 -0
- package/lib/esm/contract/index.js +17 -18
- package/lib/esm/contract/index.js.map +1 -1
- package/lib/esm/contract/models/OtherAudit.js +1 -1
- package/lib/esm/contract/models/OtherAudit.js.map +1 -1
- package/lib/esm/contract/models/OtherBatchShared.js +138 -30
- package/lib/esm/contract/models/OtherBatchShared.js.map +1 -1
- package/lib/esm/contract/models/OtherLeaflet.js +1 -1
- package/lib/esm/contract/models/OtherLeaflet.js.map +1 -1
- package/lib/esm/contract/models/OtherLeafletFile.js +1 -1
- package/lib/esm/contract/models/OtherLeafletFile.js.map +1 -1
- package/lib/esm/contract/models/OtherMarket.js +1 -1
- package/lib/esm/contract/models/OtherMarket.js.map +1 -1
- package/lib/esm/contract/models/OtherProduct.js +1 -1
- package/lib/esm/contract/models/OtherProduct.js.map +1 -1
- package/lib/esm/contract/models/OtherProductImage.js +1 -1
- package/lib/esm/contract/models/OtherProductImage.js.map +1 -1
- package/lib/esm/contract/models/OtherProductShared.js +1 -1
- package/lib/esm/contract/models/OtherProductShared.js.map +1 -1
- package/lib/esm/contract/models/OtherProductStrength.js +1 -3
- package/lib/esm/contract/models/OtherProductStrength.js.map +1 -1
- package/lib/esm/contract/trackedModels/History.js +72 -0
- package/lib/esm/contract/trackedModels/History.js.map +1 -0
- package/lib/esm/contract/trackedModels/OtherAudit.js +115 -0
- package/lib/esm/contract/trackedModels/OtherAudit.js.map +1 -0
- package/lib/esm/contract/trackedModels/OtherLeaflet.js +112 -0
- package/lib/esm/contract/trackedModels/OtherLeaflet.js.map +1 -0
- package/lib/esm/contract/trackedModels/OtherLeafletFile.js +62 -0
- package/lib/esm/contract/trackedModels/OtherLeafletFile.js.map +1 -0
- package/lib/esm/contract/trackedModels/OtherMarket.js +81 -0
- package/lib/esm/contract/trackedModels/OtherMarket.js.map +1 -0
- package/lib/esm/contract/trackedModels/OtherProduct.js +74 -0
- package/lib/esm/contract/trackedModels/OtherProduct.js.map +1 -0
- package/lib/esm/contract/trackedModels/OtherProductImage.js +56 -0
- package/lib/esm/contract/trackedModels/OtherProductImage.js.map +1 -0
- package/lib/esm/contract/trackedModels/OtherProductShared.js +96 -0
- package/lib/esm/contract/trackedModels/OtherProductShared.js.map +1 -0
- package/lib/esm/contract/trackedModels/OtherProductStrength.js +82 -0
- package/lib/esm/contract/trackedModels/OtherProductStrength.js.map +1 -0
- package/lib/esm/contracts/fabric-overrides.js +2 -2
- package/lib/esm/contracts/fabric-overrides.js.map +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/types/contract/OtherLeafletContract.d.cts +18 -0
- package/lib/types/contract/OtherLeafletContract.d.mts +18 -0
- package/lib/types/contract/OtherLeafletFileContract.d.cts +17 -0
- package/lib/types/contract/OtherLeafletFileContract.d.mts +17 -0
- package/lib/types/contract/models/OtherBatchShared.d.cts +43 -6
- package/lib/types/contract/models/OtherBatchShared.d.mts +43 -6
- package/lib/types/contract/trackedModels/History.d.cts +9 -0
- package/lib/types/contract/trackedModels/History.d.mts +9 -0
- package/lib/types/contract/trackedModels/OtherAudit.d.cts +15 -0
- package/lib/types/contract/trackedModels/OtherAudit.d.mts +15 -0
- package/lib/types/contract/trackedModels/OtherLeaflet.d.cts +16 -0
- package/lib/types/contract/trackedModels/OtherLeaflet.d.mts +16 -0
- package/lib/types/contract/trackedModels/OtherLeafletFile.d.cts +10 -0
- package/lib/types/contract/trackedModels/OtherLeafletFile.d.mts +10 -0
- package/lib/types/contract/trackedModels/OtherMarket.d.cts +13 -0
- package/lib/types/contract/trackedModels/OtherMarket.d.mts +13 -0
- package/lib/types/contract/trackedModels/OtherProduct.d.cts +12 -0
- package/lib/types/contract/trackedModels/OtherProduct.d.mts +12 -0
- package/lib/types/contract/trackedModels/OtherProductImage.d.cts +9 -0
- package/lib/types/contract/trackedModels/OtherProductImage.d.mts +9 -0
- package/lib/types/contract/trackedModels/OtherProductShared.d.cts +18 -0
- package/lib/types/contract/trackedModels/OtherProductShared.d.mts +18 -0
- package/lib/types/contract/trackedModels/OtherProductStrength.d.cts +12 -0
- package/lib/types/contract/trackedModels/OtherProductStrength.d.mts +12 -0
- package/lib/types/version.d.cts +1 -1
- package/lib/types/version.d.mts +1 -1
- package/package.json +16 -5
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|

|
|
2
|
+
|
|
2
3
|
## Hyperledger Fabric Contracts for DECAF
|
|
3
4
|
|
|
4
5
|
Chaincode-side building blocks for Hyperledger Fabric written in TypeScript. This module provides repositories, CRUD contracts (object and serialized JSON), an ERC20 sample contract, Fabric-backed sequences, event emission, and context-aware logging. It adapts DECAF’s data-access patterns to Fabric’s world-state so you can write smart contracts with the same Repository/Model abstractions used elsewhere in DECAF.
|
|
5
6
|
|
|
6
7
|
> Release docs refreshed on 2025-11-26. See [workdocs/reports/RELEASE_NOTES.md](./workdocs/reports/RELEASE_NOTES.md) for ticket summaries.
|
|
7
8
|
|
|
8
|
-
|
|
9
9
|

|
|
10
10
|

|
|
11
11
|

|
|
@@ -34,6 +34,7 @@ Documentation available [here](https://decaf-ts.github.io/for-fabric/)
|
|
|
34
34
|
This module focuses on the chaincode (contracts) side of @decaf-ts/for-fabric. It adapts DECAF’s Repository/Model/Adapter abstractions to Hyperledger Fabric’s world state and execution context so you can implement smart contracts with familiar patterns and minimal boilerplate.
|
|
35
35
|
|
|
36
36
|
Key ideas:
|
|
37
|
+
|
|
37
38
|
- Keep your domain models as annotated classes (using @decaf-ts/decorator-validation).
|
|
38
39
|
- Use a Repository to persist/read/query models through a Fabric-aware Adapter.
|
|
39
40
|
- Compose reusable CRUD contracts and utilities instead of hand-writing stub calls.
|
|
@@ -42,31 +43,38 @@ Key ideas:
|
|
|
42
43
|
|
|
43
44
|
Contracts building blocks identified in src/contracts:
|
|
44
45
|
|
|
45
|
-
1
|
|
46
|
+
1. Core context and types
|
|
47
|
+
|
|
46
48
|
- FabricContractContext: Extends the generic Context to expose Fabric-specific properties (stub, clientIdentity, logger) and timestamp resolution from the ledger.
|
|
47
49
|
- FabricContractFlags: Interface extending RepositoryFlags with stub, clientIdentity and logger for contract calls.
|
|
48
50
|
|
|
49
|
-
2
|
|
51
|
+
2. Logging
|
|
52
|
+
|
|
50
53
|
- ContractLogger: MiniLogger-compatible logger bound to the Fabric contract Context. It honors log levels and forwards to the underlying Fabric logger.
|
|
51
54
|
|
|
52
|
-
3
|
|
55
|
+
3. Adapter and repository
|
|
56
|
+
|
|
53
57
|
- FabricContractAdapter: Chaincode-side Adapter that implements CRUD, bulk operations, raw Mango queries, result iteration, model preparation/reversion, composite-key prefixes, and sequence creation. Bridges DECAF abstractions to Fabric (ChaincodeStub, ClientIdentity) and CouchDB-style queries.
|
|
54
58
|
- FabricContractRepository<M>: Repository for models inside chaincode. Supports create, update, createAll, updateAll, read/readAll, raw queries (Mango), select projections, prefix-based bulk ops, and event emission through an ObserverHandler.
|
|
55
59
|
- FabricContractRepositoryObservableHandler: ObserverHandler that emits Fabric events via stub.setEvent using names generated by generateFabricEventName.
|
|
56
60
|
|
|
57
|
-
4
|
|
61
|
+
4. Sequences
|
|
62
|
+
|
|
58
63
|
- FabricContractDBSequence: Fabric-backed implementation of Sequence with current, next and range. Stores values in the world state via FabricContractRepository and supports Number or BigInt sequences with configurable startWith and incrementBy.
|
|
59
64
|
|
|
60
|
-
5
|
|
65
|
+
5. CRUD contracts
|
|
66
|
+
|
|
61
67
|
- FabricCrudContract<M>: Base smart contract exposing CRUD endpoints (create, read, update, delete, createAll, readAll, updateAll, deleteAll, raw, init, healthcheck) for a model type. Uses DeterministicSerializer and the FabricContractAdapter/Repository behind the scenes and provides logFor(ctx).
|
|
62
68
|
- SerializedCrudContract<M>: Same endpoints as FabricCrudContract but takes/returns JSON strings (de)serialized to the model class. This simplifies client interactions and is used in tests.
|
|
63
69
|
|
|
64
|
-
6
|
|
70
|
+
6. ERC20 sample
|
|
71
|
+
|
|
65
72
|
- ERC20Token, ERC20Wallet, Allowance: Sample domain models for an ERC20-like token, wallets and allowances.
|
|
66
73
|
- FabricStatement<M,R>: A CouchDBStatement bridge that runs Mango queries through FabricContractAdapter, handling primary key projection when needed.
|
|
67
|
-
- FabricERC20Contract: A full ERC20 smart contract showcasing repository-based persistence and arithmetic helpers. Implements Initialize, CheckInitialized, TokenName, Symbol, Decimals, TotalSupply, BalanceOf, Transfer, TransferFrom, Approve, Allowance, Mint, Burn, BurnFrom, ClientAccountBalance, ClientAccountID and an internal _transfer helper.
|
|
74
|
+
- FabricERC20Contract: A full ERC20 smart contract showcasing repository-based persistence and arithmetic helpers. Implements Initialize, CheckInitialized, TokenName, Symbol, Decimals, TotalSupply, BalanceOf, Transfer, TransferFrom, Approve, Allowance, Mint, Burn, BurnFrom, ClientAccountBalance, ClientAccountID and an internal \_transfer helper.
|
|
68
75
|
|
|
69
76
|
Design notes:
|
|
77
|
+
|
|
70
78
|
- Deterministic serialization is used to ensure stable bytes for world-state writes.
|
|
71
79
|
- onCreate/onCreateUpdate hooks from db-decorators are leveraged by the adapter to set primary keys and creator/owner metadata.
|
|
72
80
|
- Mango queries (CouchDB) are used for rich queries via getQueryResultWithPagination.
|
|
@@ -74,7 +82,6 @@ Design notes:
|
|
|
74
82
|
|
|
75
83
|
With these components you can build robust chaincode while keeping code concise, testable, and aligned with DECAF’s architecture.
|
|
76
84
|
|
|
77
|
-
|
|
78
85
|
### How to Use
|
|
79
86
|
|
|
80
87
|
## Installation
|
|
@@ -88,55 +95,55 @@ npm install @decaf-ts/for-fabric
|
|
|
88
95
|
### Connecting to a Fabric Network
|
|
89
96
|
|
|
90
97
|
```typescript
|
|
91
|
-
import { FabricAdapter, PeerConfig } from
|
|
98
|
+
import { FabricAdapter, PeerConfig } from "@decaf-ts/for-fabric";
|
|
92
99
|
|
|
93
100
|
// Configure connection to a Fabric peer
|
|
94
101
|
const config: PeerConfig = {
|
|
95
|
-
mspId:
|
|
96
|
-
peerEndpoint:
|
|
97
|
-
channelName:
|
|
98
|
-
chaincodeName:
|
|
99
|
-
contractName:
|
|
100
|
-
tlsCertPath:
|
|
101
|
-
certDirectoryPath:
|
|
102
|
-
keyDirectoryPath:
|
|
103
|
-
cryptoPath:
|
|
104
|
-
peerHostAlias:
|
|
105
|
-
caEndpoint:
|
|
106
|
-
caTlsCertificate:
|
|
107
|
-
caCert:
|
|
108
|
-
caKey:
|
|
109
|
-
ca:
|
|
102
|
+
mspId: "Org1MSP",
|
|
103
|
+
peerEndpoint: "localhost:7051",
|
|
104
|
+
channelName: "mychannel",
|
|
105
|
+
chaincodeName: "mycc",
|
|
106
|
+
contractName: "mycontract",
|
|
107
|
+
tlsCertPath: "/path/to/tls/cert",
|
|
108
|
+
certDirectoryPath: "/path/to/cert/dir",
|
|
109
|
+
keyDirectoryPath: "/path/to/key/dir",
|
|
110
|
+
cryptoPath: "/path/to/crypto",
|
|
111
|
+
peerHostAlias: "peer0.org1.example.com",
|
|
112
|
+
caEndpoint: "localhost:7054",
|
|
113
|
+
caTlsCertificate: "/path/to/ca/tls/cert",
|
|
114
|
+
caCert: "/path/to/ca/cert",
|
|
115
|
+
caKey: "/path/to/ca/key",
|
|
116
|
+
ca: "ca.org1.example.com",
|
|
110
117
|
};
|
|
111
118
|
|
|
112
119
|
// Create an adapter instance
|
|
113
|
-
const adapter = new FabricAdapter(config,
|
|
120
|
+
const adapter = new FabricAdapter(config, "org1-adapter");
|
|
114
121
|
|
|
115
122
|
// Use the adapter to interact with the Fabric network
|
|
116
123
|
async function createAsset() {
|
|
117
|
-
const asset = { id:
|
|
118
|
-
return await adapter.create(
|
|
124
|
+
const asset = { id: "asset1", value: "Asset 1 Value" };
|
|
125
|
+
return await adapter.create("assets", "asset1", asset, {}, mySerializer);
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
async function readAsset(id: string) {
|
|
122
|
-
return await adapter.read(
|
|
129
|
+
return await adapter.read("assets", id, mySerializer);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
async function updateAsset(id: string, newValue: string) {
|
|
126
133
|
const asset = await readAsset(id);
|
|
127
134
|
asset.value = newValue;
|
|
128
|
-
return await adapter.update(
|
|
135
|
+
return await adapter.update("assets", id, asset, {}, mySerializer);
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
async function deleteAsset(id: string) {
|
|
132
|
-
return await adapter.delete(
|
|
139
|
+
return await adapter.delete("assets", id, mySerializer);
|
|
133
140
|
}
|
|
134
141
|
|
|
135
142
|
async function queryAssets(owner: string) {
|
|
136
143
|
const query = {
|
|
137
144
|
selector: {
|
|
138
|
-
owner: owner
|
|
139
|
-
}
|
|
145
|
+
owner: owner,
|
|
146
|
+
},
|
|
140
147
|
};
|
|
141
148
|
return await adapter.raw(query, true);
|
|
142
149
|
}
|
|
@@ -145,7 +152,7 @@ async function queryAssets(owner: string) {
|
|
|
145
152
|
### Listening for Chaincode Events
|
|
146
153
|
|
|
147
154
|
```typescript
|
|
148
|
-
import { FabricAdapter, FabricDispatch } from
|
|
155
|
+
import { FabricAdapter, FabricDispatch } from "@decaf-ts/for-fabric";
|
|
149
156
|
|
|
150
157
|
async function setupEventListener(config: PeerConfig) {
|
|
151
158
|
// Create a client
|
|
@@ -158,7 +165,7 @@ async function setupEventListener(config: PeerConfig) {
|
|
|
158
165
|
dispatch.configure(config);
|
|
159
166
|
|
|
160
167
|
// Register an observer for a specific table and event
|
|
161
|
-
dispatch.observe(
|
|
168
|
+
dispatch.observe("assets", "create", (id) => {
|
|
162
169
|
console.log(`Asset created: ${id}`);
|
|
163
170
|
// Fetch the new asset or update UI
|
|
164
171
|
});
|
|
@@ -174,29 +181,29 @@ async function setupEventListener(config: PeerConfig) {
|
|
|
174
181
|
### Working with Identities and Certificates
|
|
175
182
|
|
|
176
183
|
```typescript
|
|
177
|
-
import {
|
|
178
|
-
getIdentity,
|
|
179
|
-
getSigner,
|
|
180
|
-
readFile,
|
|
181
|
-
getCAUser
|
|
182
|
-
} from
|
|
184
|
+
import {
|
|
185
|
+
getIdentity,
|
|
186
|
+
getSigner,
|
|
187
|
+
readFile,
|
|
188
|
+
getCAUser,
|
|
189
|
+
} from "@decaf-ts/for-fabric";
|
|
183
190
|
|
|
184
191
|
async function setupIdentity() {
|
|
185
192
|
// Read a certificate file
|
|
186
|
-
const tlsCert = await readFile(
|
|
193
|
+
const tlsCert = await readFile("/path/to/tls/cert");
|
|
187
194
|
|
|
188
195
|
// Get an identity from a certificate directory
|
|
189
|
-
const identity = await getIdentity(
|
|
196
|
+
const identity = await getIdentity("Org1MSP", "/path/to/cert/dir");
|
|
190
197
|
|
|
191
198
|
// Get a signer from a key directory
|
|
192
|
-
const signer = await getSigner(
|
|
199
|
+
const signer = await getSigner("/path/to/key/dir");
|
|
193
200
|
|
|
194
201
|
// Create a CA user
|
|
195
202
|
const user = await getCAUser(
|
|
196
|
-
|
|
197
|
-
privateKeyPem,
|
|
198
|
-
certificatePem,
|
|
199
|
-
|
|
203
|
+
"user1",
|
|
204
|
+
privateKeyPem,
|
|
205
|
+
certificatePem,
|
|
206
|
+
"Org1MSP"
|
|
200
207
|
);
|
|
201
208
|
|
|
202
209
|
return { identity, signer, user };
|
|
@@ -208,9 +215,9 @@ async function setupIdentity() {
|
|
|
208
215
|
### Creating a Model
|
|
209
216
|
|
|
210
217
|
```typescript
|
|
211
|
-
import { Model, id, property, table } from
|
|
218
|
+
import { Model, id, property, table } from "@decaf-ts/decorator-validation";
|
|
212
219
|
|
|
213
|
-
@table(
|
|
220
|
+
@table("assets")
|
|
214
221
|
export class Asset extends Model {
|
|
215
222
|
@id()
|
|
216
223
|
id: string;
|
|
@@ -229,14 +236,17 @@ export class Asset extends Model {
|
|
|
229
236
|
### Creating a CRUD Contract
|
|
230
237
|
|
|
231
238
|
```typescript
|
|
232
|
-
import { FabricCrudContract } from
|
|
233
|
-
import { Context, Contract, Info, Transaction } from
|
|
234
|
-
import { Asset } from
|
|
235
|
-
|
|
236
|
-
@Info({
|
|
239
|
+
import { FabricCrudContract } from "@decaf-ts/for-fabric";
|
|
240
|
+
import { Context, Contract, Info, Transaction } from "fabric-contract-api";
|
|
241
|
+
import { Asset } from "./asset";
|
|
242
|
+
|
|
243
|
+
@Info({
|
|
244
|
+
title: "AssetContract",
|
|
245
|
+
description: "Smart contract for trading assets",
|
|
246
|
+
})
|
|
237
247
|
export class AssetContract extends FabricCrudContract<Asset> {
|
|
238
248
|
constructor() {
|
|
239
|
-
super(
|
|
249
|
+
super("AssetContract", Asset);
|
|
240
250
|
}
|
|
241
251
|
|
|
242
252
|
// The base class already provides standard CRUD operations:
|
|
@@ -256,7 +266,7 @@ export class AssetContract extends FabricCrudContract<Asset> {
|
|
|
256
266
|
results.push({
|
|
257
267
|
txId: value.txId,
|
|
258
268
|
timestamp: value.timestamp,
|
|
259
|
-
value: JSON.parse(value.value.toString(
|
|
269
|
+
value: JSON.parse(value.value.toString("utf8")),
|
|
260
270
|
});
|
|
261
271
|
|
|
262
272
|
result = await iterator.next();
|
|
@@ -267,7 +277,11 @@ export class AssetContract extends FabricCrudContract<Asset> {
|
|
|
267
277
|
}
|
|
268
278
|
|
|
269
279
|
@Transaction()
|
|
270
|
-
async transferAsset(
|
|
280
|
+
async transferAsset(
|
|
281
|
+
ctx: Context,
|
|
282
|
+
id: string,
|
|
283
|
+
newOwner: string
|
|
284
|
+
): Promise<Asset> {
|
|
271
285
|
const asset = await this.read(ctx, id);
|
|
272
286
|
asset.owner = newOwner;
|
|
273
287
|
return await this.update(ctx, asset);
|
|
@@ -278,14 +292,14 @@ export class AssetContract extends FabricCrudContract<Asset> {
|
|
|
278
292
|
### Using the Contract Adapter Directly
|
|
279
293
|
|
|
280
294
|
```typescript
|
|
281
|
-
import { FabricContractAdapter } from
|
|
282
|
-
import { Context, Contract, Transaction } from
|
|
295
|
+
import { FabricContractAdapter } from "@decaf-ts/for-fabric";
|
|
296
|
+
import { Context, Contract, Transaction } from "fabric-contract-api";
|
|
283
297
|
|
|
284
298
|
export class CustomContract extends Contract {
|
|
285
299
|
private adapter: FabricContractAdapter;
|
|
286
300
|
|
|
287
301
|
constructor() {
|
|
288
|
-
super(
|
|
302
|
+
super("CustomContract");
|
|
289
303
|
this.adapter = new FabricContractAdapter();
|
|
290
304
|
}
|
|
291
305
|
|
|
@@ -293,7 +307,7 @@ export class CustomContract extends Contract {
|
|
|
293
307
|
async createRecord(ctx: Context, id: string, data: string): Promise<any> {
|
|
294
308
|
const record = { id, data, timestamp: Date.now() };
|
|
295
309
|
return await this.adapter.create(
|
|
296
|
-
|
|
310
|
+
"records",
|
|
297
311
|
id,
|
|
298
312
|
record,
|
|
299
313
|
{},
|
|
@@ -305,15 +319,14 @@ export class CustomContract extends Contract {
|
|
|
305
319
|
async queryRecords(ctx: Context, owner: string): Promise<any[]> {
|
|
306
320
|
const query = {
|
|
307
321
|
selector: {
|
|
308
|
-
owner: owner
|
|
309
|
-
}
|
|
322
|
+
owner: owner,
|
|
323
|
+
},
|
|
310
324
|
};
|
|
311
325
|
|
|
312
|
-
return await this.adapter.raw(
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
);
|
|
326
|
+
return await this.adapter.raw(query, true, {
|
|
327
|
+
stub: ctx.stub,
|
|
328
|
+
logger: ctx.logging,
|
|
329
|
+
});
|
|
317
330
|
}
|
|
318
331
|
}
|
|
319
332
|
```
|
|
@@ -321,26 +334,22 @@ export class CustomContract extends Contract {
|
|
|
321
334
|
### Emitting and Handling Events
|
|
322
335
|
|
|
323
336
|
```typescript
|
|
324
|
-
import {
|
|
337
|
+
import {
|
|
325
338
|
FabricContractRepositoryObservableHandler,
|
|
326
339
|
generateFabricEventName,
|
|
327
|
-
parseEventName
|
|
328
|
-
} from
|
|
329
|
-
import { Context } from
|
|
330
|
-
import { OperationKeys } from
|
|
340
|
+
parseEventName,
|
|
341
|
+
} from "@decaf-ts/for-fabric";
|
|
342
|
+
import { Context } from "fabric-contract-api";
|
|
343
|
+
import { OperationKeys } from "@decaf-ts/db-decorators";
|
|
331
344
|
|
|
332
345
|
// In chaincode: Emit an event
|
|
333
346
|
async function emitEvent(ctx: Context, tableName: string, id: string) {
|
|
334
347
|
const handler = new FabricContractRepositoryObservableHandler();
|
|
335
|
-
const logger = ctx.logging.getLogger(
|
|
348
|
+
const logger = ctx.logging.getLogger("EventHandler");
|
|
336
349
|
|
|
337
|
-
await handler.updateObservers(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
OperationKeys.CREATE,
|
|
341
|
-
id,
|
|
342
|
-
{ stub: ctx.stub }
|
|
343
|
-
);
|
|
350
|
+
await handler.updateObservers(logger, tableName, OperationKeys.CREATE, id, {
|
|
351
|
+
stub: ctx.stub,
|
|
352
|
+
});
|
|
344
353
|
}
|
|
345
354
|
|
|
346
355
|
// In client: Parse an event name
|
|
@@ -357,7 +366,6 @@ function handleEvent(eventName: string, payload: Buffer) {
|
|
|
357
366
|
|
|
358
367
|
For more detailed examples and API documentation, refer to the [API Reference](./docs/api/index.html).
|
|
359
368
|
|
|
360
|
-
|
|
361
369
|
## Contracts APIs (Chaincode)
|
|
362
370
|
|
|
363
371
|
The following examples are based on the contracts in for-fabric/src/contracts and reflect the patterns used by the unit/integration tests.
|
|
@@ -367,29 +375,31 @@ The following examples are based on the contracts in for-fabric/src/contracts an
|
|
|
367
375
|
Description: Base contract exposing CRUD endpoints for a model class. It uses Repository and DeterministicSerializer under the hood.
|
|
368
376
|
|
|
369
377
|
```typescript
|
|
370
|
-
import { Context, Transaction, Contract } from
|
|
371
|
-
import { model, ModelArg, required } from
|
|
372
|
-
import { BaseModel, pk } from
|
|
373
|
-
import { FabricCrudContract } from
|
|
378
|
+
import { Context, Transaction, Contract } from "fabric-contract-api";
|
|
379
|
+
import { model, ModelArg, required } from "@decaf-ts/decorator-validation";
|
|
380
|
+
import { BaseModel, pk } from "@decaf-ts/core";
|
|
381
|
+
import { FabricCrudContract } from "@decaf-ts/for-fabric/contracts";
|
|
374
382
|
|
|
375
383
|
@model()
|
|
376
384
|
class Person extends BaseModel {
|
|
377
|
-
@pk({ type:
|
|
385
|
+
@pk({ type: "Number" })
|
|
378
386
|
id!: number;
|
|
379
387
|
@required() name!: string;
|
|
380
|
-
constructor(arg?: ModelArg<Person>) {
|
|
388
|
+
constructor(arg?: ModelArg<Person>) {
|
|
389
|
+
super(arg);
|
|
390
|
+
}
|
|
381
391
|
}
|
|
382
392
|
|
|
383
393
|
export class PersonContract extends FabricCrudContract<Person> {
|
|
384
394
|
constructor() {
|
|
385
|
-
super(
|
|
395
|
+
super("PersonContract", Person);
|
|
386
396
|
}
|
|
387
397
|
|
|
388
398
|
@Transaction(false)
|
|
389
399
|
async ping(ctx: Context): Promise<string> {
|
|
390
400
|
// Uses FabricCrudContract.logFor
|
|
391
|
-
this.logFor(ctx).info(
|
|
392
|
-
return
|
|
401
|
+
this.logFor(ctx).info("ping");
|
|
402
|
+
return "pong";
|
|
393
403
|
}
|
|
394
404
|
}
|
|
395
405
|
```
|
|
@@ -401,28 +411,33 @@ Usage in tests: see tests/unit/contracts.test.ts pattern where a SerializedCrudC
|
|
|
401
411
|
Description: Same endpoints as FabricCrudContract but takes and returns JSON strings. Useful for simple clients. Based on tests/unit/contracts.test.ts.
|
|
402
412
|
|
|
403
413
|
```typescript
|
|
404
|
-
import { Context } from
|
|
405
|
-
import { model, ModelArg, required } from
|
|
406
|
-
import { BaseModel, pk } from
|
|
407
|
-
import { SerializedCrudContract } from
|
|
414
|
+
import { Context } from "fabric-contract-api";
|
|
415
|
+
import { model, ModelArg, required } from "@decaf-ts/decorator-validation";
|
|
416
|
+
import { BaseModel, pk } from "@decaf-ts/core";
|
|
417
|
+
import { SerializedCrudContract } from "@decaf-ts/for-fabric/contracts";
|
|
408
418
|
|
|
409
419
|
@model()
|
|
410
420
|
class TestModel extends BaseModel {
|
|
411
|
-
@pk({ type:
|
|
421
|
+
@pk({ type: "Number" }) id!: number;
|
|
412
422
|
@required() name!: string;
|
|
413
423
|
@required() nif!: string;
|
|
414
|
-
constructor(arg?: ModelArg<TestModel>) {
|
|
424
|
+
constructor(arg?: ModelArg<TestModel>) {
|
|
425
|
+
super(arg);
|
|
426
|
+
}
|
|
415
427
|
}
|
|
416
428
|
|
|
417
429
|
export class TestModelContract extends SerializedCrudContract<TestModel> {
|
|
418
430
|
constructor() {
|
|
419
|
-
super(
|
|
431
|
+
super("TestModelContract", TestModel);
|
|
420
432
|
}
|
|
421
433
|
}
|
|
422
434
|
|
|
423
435
|
// Example invocation (mirrors unit test usage)
|
|
424
436
|
async function createExample(contract: TestModelContract, ctx: Context) {
|
|
425
|
-
const payload = new TestModel({
|
|
437
|
+
const payload = new TestModel({
|
|
438
|
+
name: "Alice",
|
|
439
|
+
nif: "123456789",
|
|
440
|
+
}).serialize();
|
|
426
441
|
const resultJson = await contract.create(ctx, payload);
|
|
427
442
|
const created = new TestModel(JSON.parse(resultJson));
|
|
428
443
|
return created;
|
|
@@ -434,24 +449,29 @@ async function createExample(contract: TestModelContract, ctx: Context) {
|
|
|
434
449
|
Description: Chaincode-side repository used inside contract methods to persist and query models.
|
|
435
450
|
|
|
436
451
|
```typescript
|
|
437
|
-
import { Context } from
|
|
438
|
-
import { Repo } from
|
|
439
|
-
import { model, required, ModelArg } from
|
|
440
|
-
import { BaseModel, pk } from
|
|
441
|
-
import { FabricContractRepository } from
|
|
452
|
+
import { Context } from "fabric-contract-api";
|
|
453
|
+
import { Repo } from "@decaf-ts/core";
|
|
454
|
+
import { model, required, ModelArg } from "@decaf-ts/decorator-validation";
|
|
455
|
+
import { BaseModel, pk } from "@decaf-ts/core";
|
|
456
|
+
import { FabricContractRepository } from "@decaf-ts/for-fabric/contracts";
|
|
442
457
|
|
|
443
458
|
@model()
|
|
444
459
|
class Asset extends BaseModel {
|
|
445
460
|
@pk() id!: string;
|
|
446
461
|
@required() owner!: string;
|
|
447
|
-
constructor(arg?: ModelArg<Asset>) {
|
|
462
|
+
constructor(arg?: ModelArg<Asset>) {
|
|
463
|
+
super(arg);
|
|
464
|
+
}
|
|
448
465
|
}
|
|
449
466
|
|
|
450
467
|
export class AssetContract extends Contract {
|
|
451
468
|
private repo: Repo<Asset, any, any, any, any>;
|
|
452
469
|
constructor() {
|
|
453
|
-
super(
|
|
454
|
-
this.repo = new FabricContractRepository<Asset>(
|
|
470
|
+
super("AssetContract");
|
|
471
|
+
this.repo = new FabricContractRepository<Asset>(
|
|
472
|
+
new (require("@decaf-ts/for-fabric").contracts.FabricContractAdapter)(),
|
|
473
|
+
Asset
|
|
474
|
+
);
|
|
455
475
|
}
|
|
456
476
|
|
|
457
477
|
@Transaction()
|
|
@@ -477,19 +497,22 @@ export class AssetContract extends Contract {
|
|
|
477
497
|
Description: World-state backed sequences for generating incremental values.
|
|
478
498
|
|
|
479
499
|
```typescript
|
|
480
|
-
import { Context } from
|
|
481
|
-
import { FabricContractDBSequence } from
|
|
482
|
-
import { FabricContractAdapter } from
|
|
500
|
+
import { Context } from "fabric-contract-api";
|
|
501
|
+
import { FabricContractDBSequence } from "@decaf-ts/for-fabric/contracts";
|
|
502
|
+
import { FabricContractAdapter } from "@decaf-ts/for-fabric/contracts";
|
|
483
503
|
|
|
484
504
|
const adapter = new FabricContractAdapter();
|
|
485
505
|
|
|
486
506
|
export class OrderContract extends Contract {
|
|
487
|
-
private orderSeq = new FabricContractDBSequence(
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
507
|
+
private orderSeq = new FabricContractDBSequence(
|
|
508
|
+
{
|
|
509
|
+
name: "orderSeq",
|
|
510
|
+
type: "Number",
|
|
511
|
+
startWith: 1,
|
|
512
|
+
incrementBy: 1,
|
|
513
|
+
},
|
|
514
|
+
adapter
|
|
515
|
+
);
|
|
493
516
|
|
|
494
517
|
@Transaction()
|
|
495
518
|
async CreateOrder(ctx: Context): Promise<number> {
|
|
@@ -510,11 +533,11 @@ export class OrderContract extends Contract {
|
|
|
510
533
|
Description: Bridge to run Mango queries through the Fabric adapter and get typed models back; used internally by repositories and also directly in advanced cases. See tests/unit/erc20conttract.test.ts mocking CouchDBStatement processing.
|
|
511
534
|
|
|
512
535
|
```typescript
|
|
513
|
-
import { FabricStatement } from
|
|
514
|
-
import { FabricContractAdapter } from
|
|
515
|
-
import { FabricContractContext } from
|
|
516
|
-
import { MangoQuery } from
|
|
517
|
-
import { Model } from
|
|
536
|
+
import { FabricStatement } from "@decaf-ts/for-fabric/contracts";
|
|
537
|
+
import { FabricContractAdapter } from "@decaf-ts/for-fabric/contracts";
|
|
538
|
+
import { FabricContractContext } from "@decaf-ts/for-fabric/contracts";
|
|
539
|
+
import { MangoQuery } from "@decaf-ts/for-couchdb";
|
|
540
|
+
import { Model } from "@decaf-ts/decorator-validation";
|
|
518
541
|
|
|
519
542
|
class MyModel extends Model {}
|
|
520
543
|
|
|
@@ -522,7 +545,9 @@ const adapter = new FabricContractAdapter();
|
|
|
522
545
|
|
|
523
546
|
async function query(ctx: FabricContractContext) {
|
|
524
547
|
const stmt = new FabricStatement<MyModel, MyModel[]>(adapter, ctx);
|
|
525
|
-
const models = await stmt.raw<MyModel[]>({
|
|
548
|
+
const models = await stmt.raw<MyModel[]>({
|
|
549
|
+
selector: { type: "MyModel" },
|
|
550
|
+
} as MangoQuery);
|
|
526
551
|
return models;
|
|
527
552
|
}
|
|
528
553
|
```
|
|
@@ -532,17 +557,21 @@ async function query(ctx: FabricContractContext) {
|
|
|
532
557
|
Description: Context-aware logger bound to Fabric’s Context, honoring log levels.
|
|
533
558
|
|
|
534
559
|
```typescript
|
|
535
|
-
import { Context, Transaction } from
|
|
536
|
-
import { Contract } from
|
|
537
|
-
import { ContractLogger } from
|
|
560
|
+
import { Context, Transaction } from "fabric-contract-api";
|
|
561
|
+
import { Contract } from "fabric-contract-api";
|
|
562
|
+
import { ContractLogger } from "@decaf-ts/for-fabric/contracts";
|
|
538
563
|
|
|
539
564
|
export class LoggableContract extends Contract {
|
|
540
565
|
@Transaction()
|
|
541
566
|
async DoWork(ctx: Context): Promise<void> {
|
|
542
|
-
const log = new ContractLogger(
|
|
543
|
-
|
|
567
|
+
const log = new ContractLogger(
|
|
568
|
+
"LoggableContract",
|
|
569
|
+
{ level: "info" },
|
|
570
|
+
ctx as any
|
|
571
|
+
);
|
|
572
|
+
log.info("Starting work");
|
|
544
573
|
// ... work ...
|
|
545
|
-
log.debug(
|
|
574
|
+
log.debug("Finished");
|
|
546
575
|
}
|
|
547
576
|
}
|
|
548
577
|
```
|
|
@@ -552,15 +581,21 @@ export class LoggableContract extends Contract {
|
|
|
552
581
|
Description: Emits Fabric events for repository operations. You can also use it directly to emit a custom event.
|
|
553
582
|
|
|
554
583
|
```typescript
|
|
555
|
-
import { FabricContractRepositoryObservableHandler } from
|
|
556
|
-
import { OperationKeys } from
|
|
557
|
-
import { FabricContractContext } from
|
|
558
|
-
import { MiniLogger } from
|
|
584
|
+
import { FabricContractRepositoryObservableHandler } from "@decaf-ts/for-fabric/contracts";
|
|
585
|
+
import { OperationKeys } from "@decaf-ts/db-decorators";
|
|
586
|
+
import { FabricContractContext } from "@decaf-ts/for-fabric/contracts";
|
|
587
|
+
import { MiniLogger } from "@decaf-ts/logging";
|
|
559
588
|
|
|
560
589
|
async function emitExample(ctx: FabricContractContext) {
|
|
561
590
|
const handler = new FabricContractRepositoryObservableHandler();
|
|
562
|
-
const log = new MiniLogger(
|
|
563
|
-
await handler.updateObservers(
|
|
591
|
+
const log = new MiniLogger("obs");
|
|
592
|
+
await handler.updateObservers(
|
|
593
|
+
log as any,
|
|
594
|
+
"assets",
|
|
595
|
+
OperationKeys.CREATE,
|
|
596
|
+
"asset1",
|
|
597
|
+
ctx
|
|
598
|
+
);
|
|
564
599
|
}
|
|
565
600
|
```
|
|
566
601
|
|
|
@@ -569,7 +604,7 @@ async function emitExample(ctx: FabricContractContext) {
|
|
|
569
604
|
Description: Access Fabric-specific context inside contracts.
|
|
570
605
|
|
|
571
606
|
```typescript
|
|
572
|
-
import { FabricContractContext } from
|
|
607
|
+
import { FabricContractContext } from "@decaf-ts/for-fabric/contracts";
|
|
573
608
|
|
|
574
609
|
function readContext(ctx: FabricContractContext) {
|
|
575
610
|
const ts = ctx.timestamp; // Date from stub.getDateTimestamp()
|
|
@@ -583,19 +618,19 @@ function readContext(ctx: FabricContractContext) {
|
|
|
583
618
|
Description: Full ERC20 implementation used in tests (see tests/unit/erc20conttract.test.ts).
|
|
584
619
|
|
|
585
620
|
```typescript
|
|
586
|
-
import { FabricERC20Contract } from
|
|
587
|
-
import { FabricContractContext } from
|
|
621
|
+
import { FabricERC20Contract } from "@decaf-ts/for-fabric/contracts";
|
|
622
|
+
import { FabricContractContext } from "@decaf-ts/for-fabric/contracts";
|
|
588
623
|
|
|
589
|
-
const contract = new FabricERC20Contract(
|
|
624
|
+
const contract = new FabricERC20Contract("TestToken");
|
|
590
625
|
|
|
591
626
|
async function initAndRead(ctx: FabricContractContext) {
|
|
592
|
-
const created = await contract.Initialize(ctx,
|
|
627
|
+
const created = await contract.Initialize(ctx, "TestToken", "TT", 18);
|
|
593
628
|
if (created) {
|
|
594
629
|
const name = await contract.TokenName(ctx);
|
|
595
630
|
const decimals = await contract.Decimals(ctx);
|
|
596
631
|
return { name, decimals };
|
|
597
632
|
}
|
|
598
|
-
throw new Error(
|
|
633
|
+
throw new Error("Init failed");
|
|
599
634
|
}
|
|
600
635
|
```
|
|
601
636
|
|
|
@@ -607,7 +642,6 @@ async function initAndRead(ctx: FabricContractContext) {
|
|
|
607
642
|
|
|
608
643
|
These patterns are mirrored in the examples above to ensure correctness and consistency with the repository’s test suite.
|
|
609
644
|
|
|
610
|
-
|
|
611
645
|
## Coding Principles
|
|
612
646
|
|
|
613
647
|
- group similar functionality in folders (analog to namespaces but without any namespace declaration)
|
|
@@ -626,8 +660,8 @@ These patterns are mirrored in the examples above to ensure correctness and cons
|
|
|
626
660
|
- etc;
|
|
627
661
|
|
|
628
662
|
## Release Documentation Hooks
|
|
629
|
-
Stay aligned with the automated release pipeline by reviewing [Release Notes](./workdocs/reports/RELEASE_NOTES.md) and [Dependencies](./workdocs/reports/DEPENDENCIES.md) after trying these recipes (updated on 2025-11-26).
|
|
630
663
|
|
|
664
|
+
Stay aligned with the automated release pipeline by reviewing [Release Notes](./workdocs/reports/RELEASE_NOTES.md) and [Dependencies](./workdocs/reports/DEPENDENCIES.md) after trying these recipes (updated on 2025-11-26).
|
|
631
665
|
|
|
632
666
|
### Related
|
|
633
667
|
|
|
@@ -637,9 +671,6 @@ Stay aligned with the automated release pipeline by reviewing [Release Notes](./
|
|
|
637
671
|
|
|
638
672
|
[](https://www.linkedin.com/in/decaf-ts/)
|
|
639
673
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
674
|
#### Languages
|
|
644
675
|
|
|
645
676
|

|