@neuraiproject/neurai-assets 1.0.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.
Files changed (58) hide show
  1. package/README.md +522 -0
  2. package/examples/01-create-root-asset.js +71 -0
  3. package/examples/02-create-sub-asset.js +79 -0
  4. package/examples/03-create-nfts.js +140 -0
  5. package/examples/04-reissue-asset.js +164 -0
  6. package/examples/05-create-qualifier-and-tag.js +209 -0
  7. package/examples/06-create-restricted-asset.js +223 -0
  8. package/examples/07-freeze-and-unfreeze.js +292 -0
  9. package/examples/08-query-assets.js +332 -0
  10. package/examples/09-wallet-integration.js +320 -0
  11. package/examples/README.md +319 -0
  12. package/package.json +43 -0
  13. package/src/NeuraiAssets.js +468 -0
  14. package/src/builders/BaseAssetTransactionBuilder.js +303 -0
  15. package/src/builders/FreezeAddressBuilder.js +271 -0
  16. package/src/builders/IssueQualifierBuilder.js +251 -0
  17. package/src/builders/IssueRestrictedBuilder.js +187 -0
  18. package/src/builders/IssueRootBuilder.js +173 -0
  19. package/src/builders/IssueSubBuilder.js +237 -0
  20. package/src/builders/IssueUniqueBuilder.js +255 -0
  21. package/src/builders/ReissueBuilder.js +246 -0
  22. package/src/builders/ReissueRestrictedBuilder.js +264 -0
  23. package/src/builders/TagAddressBuilder.js +243 -0
  24. package/src/builders/index.js +38 -0
  25. package/src/constants/assetTypes.js +23 -0
  26. package/src/constants/burnAddresses.js +65 -0
  27. package/src/constants/fees.js +61 -0
  28. package/src/constants/index.js +44 -0
  29. package/src/constants/networks.js +112 -0
  30. package/src/errors/AssetErrors.js +135 -0
  31. package/src/errors/ValidationErrors.js +87 -0
  32. package/src/errors/index.js +56 -0
  33. package/src/index.js +68 -0
  34. package/src/managers/BurnManager.js +222 -0
  35. package/src/managers/OutputOrderer.js +289 -0
  36. package/src/managers/OwnerTokenManager.js +265 -0
  37. package/src/managers/UTXOSelector.js +309 -0
  38. package/src/managers/index.js +16 -0
  39. package/src/queries/AssetQueries.js +447 -0
  40. package/src/queries/index.js +10 -0
  41. package/src/utils/amountConverter.js +115 -0
  42. package/src/utils/assetNameParser.js +203 -0
  43. package/src/utils/index.js +16 -0
  44. package/src/utils/networkDetector.js +144 -0
  45. package/src/utils/outputFormatter.js +292 -0
  46. package/src/validators/amountValidator.js +149 -0
  47. package/src/validators/assetNameValidator.js +296 -0
  48. package/src/validators/index.js +16 -0
  49. package/src/validators/ipfsValidator.js +101 -0
  50. package/src/validators/verifierValidator.js +146 -0
  51. package/tests/README.md +126 -0
  52. package/tests/integration/assetLifecycle.test.js +244 -0
  53. package/tests/mocks/rpcMock.js +156 -0
  54. package/tests/unit/NeuraiAssets.test.js +217 -0
  55. package/tests/unit/utils/amountConverter.test.js +171 -0
  56. package/tests/unit/utils/assetNameParser.test.js +203 -0
  57. package/tests/unit/validators/amountValidator.test.js +143 -0
  58. package/tests/unit/validators/assetNameValidator.test.js +228 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Example: Create RESTRICTED Assets (Security Tokens)
3
+ *
4
+ * This example demonstrates how to create RESTRICTED assets.
5
+ * RESTRICTED assets are security tokens with built-in compliance controls.
6
+ *
7
+ * Format: $NAME (e.g., $SECURITY, $STOCK)
8
+ *
9
+ * Features:
10
+ * - Only addresses meeting verifier requirements can receive/hold
11
+ * - Can freeze individual addresses or entire asset
12
+ * - Uses QUALIFIER tags for compliance (e.g., #KYC_VERIFIED)
13
+ *
14
+ * Cost: 3000 XNA (most expensive asset type)
15
+ */
16
+
17
+ const NeuraiAssets = require('@neuraiproject/neurai-assets');
18
+
19
+ async function createRestrictedAsset() {
20
+ // Mock RPC function (replace with your actual RPC client)
21
+ const rpc = async (method, params) => {
22
+ console.log(`RPC Call: ${method}`, params);
23
+ // Your RPC implementation here
24
+ };
25
+
26
+ // Initialize NeuraiAssets
27
+ const assets = new NeuraiAssets(rpc, {
28
+ network: 'xna',
29
+ addresses: ['NYourAddress1...'],
30
+ changeAddress: 'NChangeAddress...',
31
+ toAddress: 'NReceivingAddress...'
32
+ });
33
+
34
+ try {
35
+ // Step 1: Ensure qualifiers exist
36
+ console.log('=== Step 1: Verify Qualifiers Exist ===\n');
37
+
38
+ // You need to create qualifiers first (see example 05)
39
+ const requiredQualifiers = ['#KYC_VERIFIED', '#ACCREDITED'];
40
+
41
+ for (const qualifier of requiredQualifiers) {
42
+ const exists = await assets.assetExists(qualifier);
43
+ if (!exists) {
44
+ console.warn(`⚠ Qualifier ${qualifier} does not exist. Create it first!`);
45
+ // You would need to create it using createQualifier()
46
+ } else {
47
+ console.log(`✓ Qualifier ${qualifier} exists`);
48
+ }
49
+ }
50
+
51
+ // Step 2: Create the RESTRICTED asset with verifier string
52
+ console.log('\n=== Step 2: Create RESTRICTED Asset ===\n');
53
+
54
+ const result = await assets.createRestrictedAsset({
55
+ assetName: '$SECURITY', // Must start with $
56
+ quantity: 1000000, // Total supply
57
+ units: 2, // Decimals (0-8)
58
+ verifierString: '#KYC_VERIFIED & #ACCREDITED', // Boolean logic
59
+ reissuable: true, // Allow reissuance
60
+ hasIpfs: true,
61
+ ipfsHash: 'QmSecurityTokenMetadata...'
62
+ });
63
+
64
+ console.log('RESTRICTED Asset created successfully!');
65
+ console.log('Asset Name:', '$SECURITY');
66
+ console.log('Owner Token:', '$SECURITY!');
67
+ console.log('Verifier String:', result.metadata.verifierString);
68
+ console.log('Required Qualifiers:', result.metadata.requiredQualifiers);
69
+ console.log('Burn:', result.burn, 'XNA');
70
+
71
+ console.log('\n=== Verifier String Explained ===');
72
+ console.log('Verifier: #KYC_VERIFIED & #ACCREDITED');
73
+ console.log('Meaning: Address must have BOTH tags to receive this asset');
74
+ console.log('- #KYC_VERIFIED (required)');
75
+ console.log('- #ACCREDITED (required)');
76
+
77
+ console.log('\nNext steps:');
78
+ console.log('1. Tag addresses with required qualifiers');
79
+ console.log('2. Only tagged addresses can receive $SECURITY');
80
+ console.log('3. Use owner token ($SECURITY!) to manage the asset');
81
+
82
+ } catch (error) {
83
+ console.error('Error creating RESTRICTED asset:', error.message);
84
+
85
+ if (error.name === 'InvalidVerifierStringError') {
86
+ console.error('Invalid verifier string syntax');
87
+ }
88
+ }
89
+ }
90
+
91
+ // Example: Create RESTRICTED asset with complex verifier logic
92
+ async function createRestrictedWithComplexVerifier() {
93
+ const rpc = async (method, params) => {
94
+ console.log(`RPC Call: ${method}`, params);
95
+ };
96
+
97
+ const assets = new NeuraiAssets(rpc, {
98
+ network: 'xna',
99
+ addresses: ['NYourAddress1...'],
100
+ changeAddress: 'NChangeAddress...',
101
+ toAddress: 'NReceivingAddress...'
102
+ });
103
+
104
+ try {
105
+ // Create RESTRICTED asset with OR logic
106
+ const result = await assets.createRestrictedAsset({
107
+ assetName: '$FLEXIBLE_TOKEN',
108
+ quantity: 500000,
109
+ units: 0,
110
+ // Complex verifier: (KYC AND Accredited) OR Institutional
111
+ verifierString: '(#KYC_VERIFIED & #ACCREDITED) | #INSTITUTIONAL',
112
+ reissuable: true,
113
+ hasIpfs: false,
114
+ ipfsHash: ''
115
+ });
116
+
117
+ console.log('RESTRICTED Asset with complex verifier created!');
118
+ console.log('Asset:', '$FLEXIBLE_TOKEN');
119
+ console.log('Verifier:', result.metadata.verifierString);
120
+
121
+ console.log('\n=== Verifier Logic ===');
122
+ console.log('Option 1: Address has #KYC_VERIFIED AND #ACCREDITED');
123
+ console.log('Option 2: Address has #INSTITUTIONAL');
124
+ console.log('Result: Address can receive if either option is true');
125
+
126
+ } catch (error) {
127
+ console.error('Error:', error.message);
128
+ }
129
+ }
130
+
131
+ // Example: Validate verifier string before creating asset
132
+ async function validateVerifierString() {
133
+ const rpc = async (method, params) => {
134
+ console.log(`RPC Call: ${method}`, params);
135
+ };
136
+
137
+ const assets = new NeuraiAssets(rpc, {
138
+ network: 'xna',
139
+ addresses: ['NYourAddress1...'],
140
+ changeAddress: 'NChangeAddress...',
141
+ toAddress: 'NReceivingAddress...'
142
+ });
143
+
144
+ try {
145
+ // Test different verifier strings
146
+ const verifiers = [
147
+ '#KYC_VERIFIED', // Simple
148
+ '#KYC_VERIFIED & #ACCREDITED', // AND
149
+ '#KYC_VERIFIED | #INSTITUTIONAL', // OR
150
+ '(#KYC_VERIFIED & #ACCREDITED) | #INSTITUTIONAL', // Complex
151
+ '#KYC & (#ACCREDITED | #INSTITUTIONAL)', // Nested
152
+ 'INVALID STRING', // Invalid
153
+ ];
154
+
155
+ console.log('=== Verifier String Validation ===\n');
156
+
157
+ for (const verifier of verifiers) {
158
+ const isValid = await assets.isValidVerifierString(verifier);
159
+ console.log(`${isValid ? '✓' : '✗'} "${verifier}"`);
160
+ }
161
+
162
+ } catch (error) {
163
+ console.error('Error:', error.message);
164
+ }
165
+ }
166
+
167
+ // Example: Check if address can receive restricted asset
168
+ async function checkAddressCompliance() {
169
+ const rpc = async (method, params) => {
170
+ console.log(`RPC Call: ${method}`, params);
171
+ };
172
+
173
+ const assets = new NeuraiAssets(rpc, {
174
+ network: 'xna',
175
+ addresses: ['NYourAddress1...'],
176
+ changeAddress: 'NChangeAddress...',
177
+ toAddress: 'NReceivingAddress...'
178
+ });
179
+
180
+ try {
181
+ const testAddress = 'NTestAddress...';
182
+ const restrictedAsset = '$SECURITY';
183
+
184
+ console.log('=== Compliance Check ===\n');
185
+ console.log(`Address: ${testAddress}`);
186
+ console.log(`Asset: ${restrictedAsset}`);
187
+
188
+ // Check if address meets verifier requirements
189
+ const canReceive = await assets.checkAddressRestriction(
190
+ testAddress,
191
+ restrictedAsset
192
+ );
193
+
194
+ console.log(`\nCan receive asset: ${canReceive ? 'YES ✓' : 'NO ✗'}`);
195
+
196
+ if (!canReceive) {
197
+ // Check what tags the address has
198
+ const tags = await assets.listTagsForAddress(testAddress);
199
+ console.log('\nCurrent tags:', tags.length > 0 ? tags : 'None');
200
+
201
+ // Get verifier requirements
202
+ const verifier = await assets.getVerifierString(restrictedAsset);
203
+ console.log('Required verifier:', verifier);
204
+ console.log('\nThe address needs to be tagged with required qualifiers');
205
+ }
206
+
207
+ } catch (error) {
208
+ console.error('Error:', error.message);
209
+ }
210
+ }
211
+
212
+ // Run the examples
213
+ console.log('=== Example 1: Create Simple RESTRICTED Asset ===\n');
214
+ createRestrictedAsset();
215
+
216
+ console.log('\n\n=== Example 2: Create with Complex Verifier ===\n');
217
+ createRestrictedWithComplexVerifier();
218
+
219
+ console.log('\n\n=== Example 3: Validate Verifier Strings ===\n');
220
+ validateVerifierString();
221
+
222
+ console.log('\n\n=== Example 4: Check Address Compliance ===\n');
223
+ checkAddressCompliance();
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Example: Freeze and Unfreeze Operations
3
+ *
4
+ * This example demonstrates how to freeze/unfreeze addresses and assets.
5
+ * Freezing is only available for RESTRICTED assets.
6
+ *
7
+ * Requirements:
8
+ * - Must own the restricted asset's owner token ($ASSET!)
9
+ *
10
+ * Operations:
11
+ * - Freeze specific addresses (prevent trading)
12
+ * - Unfreeze specific addresses (allow trading again)
13
+ * - Global freeze (freeze entire asset)
14
+ * - Global unfreeze (unfreeze entire asset)
15
+ *
16
+ * Cost: No burn (only network fee)
17
+ */
18
+
19
+ const NeuraiAssets = require('@neuraiproject/neurai-assets');
20
+
21
+ async function freezeAddresses() {
22
+ // Mock RPC function (replace with your actual RPC client)
23
+ const rpc = async (method, params) => {
24
+ console.log(`RPC Call: ${method}`, params);
25
+ // Your RPC implementation here
26
+ };
27
+
28
+ // Initialize NeuraiAssets
29
+ const assets = new NeuraiAssets(rpc, {
30
+ network: 'xna',
31
+ addresses: ['NYourAddress1...'],
32
+ changeAddress: 'NChangeAddress...',
33
+ toAddress: 'NReceivingAddress...'
34
+ });
35
+
36
+ try {
37
+ const restrictedAsset = '$SECURITY';
38
+ const ownerToken = `${restrictedAsset}!`;
39
+
40
+ // Step 1: Verify you own the owner token
41
+ console.log('=== Step 1: Verify Owner Token ===\n');
42
+
43
+ const myAssets = await assets.listMyAssets(ownerToken);
44
+ if (!myAssets[ownerToken]) {
45
+ throw new Error(`You must own ${ownerToken} to freeze/unfreeze`);
46
+ }
47
+
48
+ console.log(`✓ Owner token found: ${ownerToken}`);
49
+
50
+ // Step 2: Freeze specific addresses
51
+ console.log('\n=== Step 2: Freeze Addresses ===\n');
52
+
53
+ const addressesToFreeze = [
54
+ 'NAddressToFreeze1...',
55
+ 'NAddressToFreeze2...',
56
+ 'NAddressToFreeze3...'
57
+ ];
58
+
59
+ const freezeResult = await assets.freezeAddresses({
60
+ assetName: restrictedAsset,
61
+ addresses: addressesToFreeze
62
+ });
63
+
64
+ console.log('Addresses frozen successfully!');
65
+ console.log('Asset:', freezeResult.metadata.assetName);
66
+ console.log('Addresses frozen:', freezeResult.metadata.addressCount);
67
+ console.log('Fee:', freezeResult.fee, 'XNA');
68
+ console.log('Burn:', freezeResult.burn, 'XNA (no burn for freeze operations)');
69
+
70
+ console.log('\nFrozen addresses:');
71
+ freezeResult.metadata.targetAddresses.forEach((addr, i) => {
72
+ console.log(`${i + 1}. ${addr}`);
73
+ });
74
+
75
+ console.log('\nEffect: These addresses can NO LONGER:');
76
+ console.log('- Send the restricted asset');
77
+ console.log('- Receive the restricted asset');
78
+ console.log('- Trade the restricted asset');
79
+
80
+ // Step 3: Check if address is frozen
81
+ console.log('\n=== Step 3: Verify Freeze Status ===\n');
82
+
83
+ for (const address of addressesToFreeze) {
84
+ const isFrozen = await assets.isAddressFrozen(address, restrictedAsset);
85
+ console.log(`${address}: ${isFrozen ? 'FROZEN ❄️' : 'ACTIVE ✓'}`);
86
+ }
87
+
88
+ } catch (error) {
89
+ console.error('Error freezing addresses:', error.message);
90
+
91
+ if (error.name === 'OwnerTokenNotFoundError') {
92
+ console.error('You need the owner token to freeze addresses');
93
+ }
94
+ }
95
+ }
96
+
97
+ // Example: Unfreeze addresses
98
+ async function unfreezeAddresses() {
99
+ const rpc = async (method, params) => {
100
+ console.log(`RPC Call: ${method}`, params);
101
+ };
102
+
103
+ const assets = new NeuraiAssets(rpc, {
104
+ network: 'xna',
105
+ addresses: ['NYourAddress1...'],
106
+ changeAddress: 'NChangeAddress...',
107
+ toAddress: 'NReceivingAddress...'
108
+ });
109
+
110
+ try {
111
+ const restrictedAsset = '$SECURITY';
112
+ const addressesToUnfreeze = [
113
+ 'NAddressToUnfreeze1...',
114
+ 'NAddressToUnfreeze2...'
115
+ ];
116
+
117
+ console.log('=== Unfreezing Addresses ===\n');
118
+
119
+ const result = await assets.unfreezeAddresses({
120
+ assetName: restrictedAsset,
121
+ addresses: addressesToUnfreeze
122
+ });
123
+
124
+ console.log('Addresses unfrozen successfully!');
125
+ console.log('Addresses unfrozen:', result.metadata.addressCount);
126
+ console.log('Fee:', result.fee, 'XNA');
127
+
128
+ console.log('\nThese addresses can now:');
129
+ console.log('- Send the restricted asset');
130
+ console.log('- Receive the restricted asset');
131
+ console.log('- Trade the restricted asset');
132
+ console.log('(if they still meet verifier requirements)');
133
+
134
+ } catch (error) {
135
+ console.error('Error unfreezing addresses:', error.message);
136
+ }
137
+ }
138
+
139
+ // Example: Global freeze (freeze entire asset)
140
+ async function freezeAssetGlobally() {
141
+ const rpc = async (method, params) => {
142
+ console.log(`RPC Call: ${method}`, params);
143
+ };
144
+
145
+ const assets = new NeuraiAssets(rpc, {
146
+ network: 'xna',
147
+ addresses: ['NYourAddress1...'],
148
+ changeAddress: 'NChangeAddress...',
149
+ toAddress: 'NReceivingAddress...'
150
+ });
151
+
152
+ try {
153
+ const restrictedAsset = '$SECURITY';
154
+
155
+ console.log('=== Global Asset Freeze ===\n');
156
+ console.log('⚠️ WARNING: This will freeze the ENTIRE asset!');
157
+ console.log('ALL addresses will be unable to trade this asset.\n');
158
+
159
+ const result = await assets.freezeAssetGlobally({
160
+ assetName: restrictedAsset
161
+ });
162
+
163
+ console.log('Asset frozen globally!');
164
+ console.log('Asset:', result.metadata.assetName);
165
+ console.log('Operation:', result.metadata.operationType);
166
+ console.log('Fee:', result.fee, 'XNA');
167
+
168
+ console.log('\nEffect:');
169
+ console.log('- ALL addresses are frozen for this asset');
170
+ console.log('- Nobody can send/receive/trade');
171
+ console.log('- Use for emergency situations or regulatory compliance');
172
+
173
+ // Verify global freeze status
174
+ const isGloballyFrozen = await assets.checkGlobalRestriction(restrictedAsset);
175
+ console.log(`\nGlobal freeze status: ${isGloballyFrozen ? 'FROZEN ❄️' : 'ACTIVE ✓'}`);
176
+
177
+ } catch (error) {
178
+ console.error('Error freezing asset globally:', error.message);
179
+ }
180
+ }
181
+
182
+ // Example: Global unfreeze (unfreeze entire asset)
183
+ async function unfreezeAssetGlobally() {
184
+ const rpc = async (method, params) => {
185
+ console.log(`RPC Call: ${method}`, params);
186
+ };
187
+
188
+ const assets = new NeuraiAssets(rpc, {
189
+ network: 'xna',
190
+ addresses: ['NYourAddress1...'],
191
+ changeAddress: 'NChangeAddress...',
192
+ toAddress: 'NReceivingAddress...'
193
+ });
194
+
195
+ try {
196
+ const restrictedAsset = '$SECURITY';
197
+
198
+ console.log('=== Global Asset Unfreeze ===\n');
199
+
200
+ const result = await assets.unfreezeAssetGlobally({
201
+ assetName: restrictedAsset
202
+ });
203
+
204
+ console.log('Asset unfrozen globally!');
205
+ console.log('Asset:', result.metadata.assetName);
206
+ console.log('Fee:', result.fee, 'XNA');
207
+
208
+ console.log('\nEffect:');
209
+ console.log('- Global freeze lifted');
210
+ console.log('- Trading resumes for all addresses');
211
+ console.log('- Individual freezes (if any) remain in effect');
212
+ console.log('- Verifier requirements still apply');
213
+
214
+ } catch (error) {
215
+ console.error('Error unfreezing asset globally:', error.message);
216
+ }
217
+ }
218
+
219
+ // Example: Complete freeze workflow
220
+ async function completeWorkflow() {
221
+ const rpc = async (method, params) => {
222
+ console.log(`RPC Call: ${method}`, params);
223
+ };
224
+
225
+ const assets = new NeuraiAssets(rpc, {
226
+ network: 'xna',
227
+ addresses: ['NYourAddress1...'],
228
+ changeAddress: 'NChangeAddress...',
229
+ toAddress: 'NReceivingAddress...'
230
+ });
231
+
232
+ try {
233
+ const restrictedAsset = '$SECURITY';
234
+ const suspiciousAddress = 'NSuspiciousAddress...';
235
+
236
+ console.log('=== Complete Freeze/Unfreeze Workflow ===\n');
237
+
238
+ // Scenario: Detect suspicious activity
239
+ console.log('Scenario: Suspicious activity detected on address');
240
+ console.log(`Address: ${suspiciousAddress}\n`);
241
+
242
+ // Step 1: Freeze the suspicious address
243
+ console.log('Step 1: Freeze suspicious address');
244
+ await assets.freezeAddresses({
245
+ assetName: restrictedAsset,
246
+ addresses: [suspiciousAddress]
247
+ });
248
+ console.log('✓ Address frozen\n');
249
+
250
+ // Step 2: Verify freeze status
251
+ console.log('Step 2: Verify freeze status');
252
+ const isFrozen = await assets.isAddressFrozen(suspiciousAddress, restrictedAsset);
253
+ console.log(`Status: ${isFrozen ? 'FROZEN ❄️' : 'ACTIVE ✓'}\n`);
254
+
255
+ // Step 3: Investigate...
256
+ console.log('Step 3: Investigation period...\n');
257
+
258
+ // Step 4: If cleared, unfreeze
259
+ console.log('Step 4: Investigation complete - address cleared');
260
+ await assets.unfreezeAddresses({
261
+ assetName: restrictedAsset,
262
+ addresses: [suspiciousAddress]
263
+ });
264
+ console.log('✓ Address unfrozen\n');
265
+
266
+ // Step 5: Verify unfreeze
267
+ console.log('Step 5: Verify final status');
268
+ const isFrozenAfter = await assets.isAddressFrozen(suspiciousAddress, restrictedAsset);
269
+ console.log(`Status: ${isFrozenAfter ? 'FROZEN ❄️' : 'ACTIVE ✓'}\n`);
270
+
271
+ console.log('Workflow completed successfully!');
272
+
273
+ } catch (error) {
274
+ console.error('Error in workflow:', error.message);
275
+ }
276
+ }
277
+
278
+ // Run the examples
279
+ console.log('=== Example 1: Freeze Specific Addresses ===\n');
280
+ freezeAddresses();
281
+
282
+ console.log('\n\n=== Example 2: Unfreeze Addresses ===\n');
283
+ unfreezeAddresses();
284
+
285
+ console.log('\n\n=== Example 3: Global Freeze ===\n');
286
+ freezeAssetGlobally();
287
+
288
+ console.log('\n\n=== Example 4: Global Unfreeze ===\n');
289
+ unfreezeAssetGlobally();
290
+
291
+ console.log('\n\n=== Example 5: Complete Workflow ===\n');
292
+ completeWorkflow();