@layerzerolabs/gated-transaction 0.0.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/.turbo/turbo-build.log +46 -0
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-test.log +14 -0
- package/dist/5INFRBMB.js +14 -0
- package/dist/5INFRBMB.js.map +1 -0
- package/dist/AYIRG6WY.cjs +17 -0
- package/dist/AYIRG6WY.cjs.map +1 -0
- package/dist/BSVO6MXQ.cjs +84 -0
- package/dist/BSVO6MXQ.cjs.map +1 -0
- package/dist/GLQGPA3F.js +74 -0
- package/dist/GLQGPA3F.js.map +1 -0
- package/dist/PQPGPKJO.js +174 -0
- package/dist/PQPGPKJO.js.map +1 -0
- package/dist/UQ2RA5VK.cjs +181 -0
- package/dist/UQ2RA5VK.cjs.map +1 -0
- package/dist/VUOMXK5T.js +6 -0
- package/dist/VUOMXK5T.js.map +1 -0
- package/dist/YJF4D23A.cjs +8 -0
- package/dist/YJF4D23A.cjs.map +1 -0
- package/dist/gatedTx.cjs +17 -0
- package/dist/gatedTx.cjs.map +1 -0
- package/dist/gatedTx.d.ts +21 -0
- package/dist/gatedTx.d.ts.map +1 -0
- package/dist/gatedTx.js +4 -0
- package/dist/gatedTx.js.map +1 -0
- package/dist/index.cjs +47 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/resolver.cjs +19 -0
- package/dist/resolver.cjs.map +1 -0
- package/dist/resolver.d.ts +25 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +6 -0
- package/dist/resolver.js.map +1 -0
- package/dist/types.cjs +29 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
- package/src/gatedTx.ts +37 -0
- package/src/index.ts +3 -0
- package/src/resolver.ts +311 -0
- package/src/types.ts +213 -0
- package/test/resolver.test.ts +250 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +8 -0
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@layerzerolabs/gated-transaction",
|
|
3
|
+
"version": "0.0.8",
|
|
4
|
+
"private": false,
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"require": "./dist/index.cjs",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.cjs"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.cjs",
|
|
15
|
+
"module": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"lodash.isequal": "^4.5.0",
|
|
19
|
+
"zod": "^3.23.8",
|
|
20
|
+
"@layerzerolabs/common-activities": "0.0.8",
|
|
21
|
+
"@layerzerolabs/common-chain-model": "0.0.8",
|
|
22
|
+
"@layerzerolabs/common-workflow": "0.0.8",
|
|
23
|
+
"@layerzerolabs/common-utils": "0.0.8",
|
|
24
|
+
"@layerzerolabs/zod-utils": "0.0.8"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/lodash.isequal": "^4.5.8",
|
|
28
|
+
"tsup": "^8.4.0",
|
|
29
|
+
"vitest": "^3.2.3",
|
|
30
|
+
"@layerzerolabs/tsup-configuration": "0.0.8",
|
|
31
|
+
"@layerzerolabs/typescript-configuration": "0.0.8"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "restricted",
|
|
35
|
+
"registry": "https://registry.npmjs.org/"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"clean": "rm -rf ./node_modules .turbo ./dist",
|
|
40
|
+
"dev": "tsup --watch",
|
|
41
|
+
"lint": "eslint . --max-warnings 0",
|
|
42
|
+
"lint:fix": "eslint . --fix --max-warnings 0",
|
|
43
|
+
"test": "vitest --run --pass-with-no-tests"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/gatedTx.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Transaction } from '@layerzerolabs/common-chain-model';
|
|
2
|
+
import type { FunctionPointer } from '@layerzerolabs/common-utils';
|
|
3
|
+
|
|
4
|
+
import type { GatedTransaction, GatedTransactionId, OnChainDataComparison } from './types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builds a {@link GatedTransaction}
|
|
8
|
+
*/
|
|
9
|
+
export const constructGatedTransaction = <
|
|
10
|
+
const Name extends string,
|
|
11
|
+
Tx extends Transaction,
|
|
12
|
+
Method extends (...args: any[]) => any,
|
|
13
|
+
>(gtx: {
|
|
14
|
+
name: Name;
|
|
15
|
+
bundleName?: string;
|
|
16
|
+
transaction: Tx;
|
|
17
|
+
check: {
|
|
18
|
+
functionPointer: FunctionPointer<Method>;
|
|
19
|
+
params: Parameters<Method>;
|
|
20
|
+
expectedResult: OnChainDataComparison<Awaited<ReturnType<Method>>>;
|
|
21
|
+
};
|
|
22
|
+
uniqueIdKeys?: Record<string, string | number | boolean>;
|
|
23
|
+
cacheable?: boolean;
|
|
24
|
+
dependencies?: GatedTransaction[];
|
|
25
|
+
}): GatedTransaction<Name, Tx, Method> => {
|
|
26
|
+
return gtx;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const getIdForGatedTransaction = (gtx: GatedTransaction): GatedTransactionId => {
|
|
30
|
+
const serializedUniqueIdKeys = gtx.uniqueIdKeys
|
|
31
|
+
? Object.entries(gtx.uniqueIdKeys)
|
|
32
|
+
.map(([k, v]) => `${k}:${v}`)
|
|
33
|
+
.join('-')
|
|
34
|
+
: '';
|
|
35
|
+
|
|
36
|
+
return `${gtx.name}-${gtx.transaction.chainName}-${serializedUniqueIdKeys}`;
|
|
37
|
+
};
|
package/src/index.ts
ADDED
package/src/resolver.ts
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import isEqual from 'lodash.isequal';
|
|
2
|
+
|
|
3
|
+
import type { ActivityRegistry } from '@layerzerolabs/common-activities';
|
|
4
|
+
import type { Transaction } from '@layerzerolabs/common-chain-model';
|
|
5
|
+
import type { WorkflowFunctions } from '@layerzerolabs/common-workflow';
|
|
6
|
+
|
|
7
|
+
import { getIdForGatedTransaction } from './gatedTx';
|
|
8
|
+
import type {
|
|
9
|
+
GatedTransaction,
|
|
10
|
+
GatedTransactionId,
|
|
11
|
+
GatedTransactionResult,
|
|
12
|
+
IGatedTransactionCache,
|
|
13
|
+
InferOnChainDataTypeFromGatedTransaction,
|
|
14
|
+
UserInteractionCallbacks,
|
|
15
|
+
} from './types';
|
|
16
|
+
import { GatedTransactionStatus } from './types';
|
|
17
|
+
|
|
18
|
+
type OnChainCheckResult<DataType = any> = {
|
|
19
|
+
expectationMet: boolean;
|
|
20
|
+
onChainData: DataType;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const checkGatedTransaction = async <GatedTx extends GatedTransaction>({
|
|
24
|
+
registry,
|
|
25
|
+
gatedTransaction,
|
|
26
|
+
cachedCheckResult,
|
|
27
|
+
processedTransaction,
|
|
28
|
+
}: {
|
|
29
|
+
registry: ActivityRegistry;
|
|
30
|
+
gatedTransaction: GatedTx;
|
|
31
|
+
cachedCheckResult?: InferOnChainDataTypeFromGatedTransaction<GatedTx>;
|
|
32
|
+
processedTransaction?: any;
|
|
33
|
+
}): Promise<OnChainCheckResult<InferOnChainDataTypeFromGatedTransaction<GatedTx>>> => {
|
|
34
|
+
let onChainData: InferOnChainDataTypeFromGatedTransaction<GatedTx>;
|
|
35
|
+
if (cachedCheckResult !== undefined) {
|
|
36
|
+
onChainData = cachedCheckResult;
|
|
37
|
+
} else {
|
|
38
|
+
// Always inject the processed transaction as the last parameter when available
|
|
39
|
+
let params = gatedTransaction.check.params;
|
|
40
|
+
if (processedTransaction !== undefined) {
|
|
41
|
+
params = [...gatedTransaction.check.params, processedTransaction];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
onChainData = await registry.callByPointer(gatedTransaction.check.functionPointer, params);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let expectationMet: boolean;
|
|
48
|
+
|
|
49
|
+
switch (gatedTransaction.check.expectedResult.operator) {
|
|
50
|
+
case '=':
|
|
51
|
+
expectationMet = isEqual(
|
|
52
|
+
onChainData,
|
|
53
|
+
gatedTransaction.check.expectedResult.comparisonValue,
|
|
54
|
+
);
|
|
55
|
+
break;
|
|
56
|
+
case '!=':
|
|
57
|
+
expectationMet = !isEqual(
|
|
58
|
+
onChainData,
|
|
59
|
+
gatedTransaction.check.expectedResult.comparisonValue,
|
|
60
|
+
);
|
|
61
|
+
break;
|
|
62
|
+
case '>':
|
|
63
|
+
expectationMet =
|
|
64
|
+
(onChainData as number) >
|
|
65
|
+
(gatedTransaction.check.expectedResult.comparisonValue as number);
|
|
66
|
+
break;
|
|
67
|
+
case '>=':
|
|
68
|
+
expectationMet =
|
|
69
|
+
(onChainData as number) >=
|
|
70
|
+
(gatedTransaction.check.expectedResult.comparisonValue as number);
|
|
71
|
+
break;
|
|
72
|
+
case '<':
|
|
73
|
+
expectationMet =
|
|
74
|
+
(onChainData as number) <
|
|
75
|
+
(gatedTransaction.check.expectedResult.comparisonValue as number);
|
|
76
|
+
break;
|
|
77
|
+
case '<=':
|
|
78
|
+
expectationMet =
|
|
79
|
+
(onChainData as number) <=
|
|
80
|
+
(gatedTransaction.check.expectedResult.comparisonValue as number);
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Gated transaction operator "${gatedTransaction.check.expectedResult.operator}" is not supported`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
expectationMet,
|
|
90
|
+
onChainData,
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const resolveGatedTransaction = async <
|
|
95
|
+
TxType extends Transaction,
|
|
96
|
+
ProcessedTxType extends Transaction,
|
|
97
|
+
GatedTx extends GatedTransaction<string, TxType, any>,
|
|
98
|
+
ProcessTx extends (transaction: TxType) => Promise<ProcessedTxType>,
|
|
99
|
+
>({
|
|
100
|
+
activityRegistry,
|
|
101
|
+
gatedTx,
|
|
102
|
+
isTransactionSuccessful,
|
|
103
|
+
processTx,
|
|
104
|
+
confirmationCallback,
|
|
105
|
+
fn,
|
|
106
|
+
cache,
|
|
107
|
+
}: {
|
|
108
|
+
activityRegistry: ActivityRegistry;
|
|
109
|
+
gatedTx: GatedTx;
|
|
110
|
+
processTx: ProcessTx;
|
|
111
|
+
isTransactionSuccessful: (gtxId: GatedTransactionId) => boolean | undefined;
|
|
112
|
+
confirmationCallback: NonNullable<UserInteractionCallbacks['confirmationCallback']>;
|
|
113
|
+
fn: WorkflowFunctions;
|
|
114
|
+
cache?: IGatedTransactionCache;
|
|
115
|
+
}): Promise<GatedTx & { result: GatedTransactionResult<GatedTx, ProcessedTxType> }> => {
|
|
116
|
+
const dependencyIds = (gatedTx.dependencies ?? []).map((tx) => getIdForGatedTransaction(tx));
|
|
117
|
+
|
|
118
|
+
const dependenciesAreAllProcessed = () =>
|
|
119
|
+
dependencyIds.every((id) => isTransactionSuccessful(id) != null);
|
|
120
|
+
|
|
121
|
+
await fn.condition(dependenciesAreAllProcessed);
|
|
122
|
+
|
|
123
|
+
//check that the dependencies succeeded
|
|
124
|
+
const dependenciesWereSuccessful = dependencyIds.every((id) => isTransactionSuccessful(id));
|
|
125
|
+
|
|
126
|
+
if (!dependenciesWereSuccessful) {
|
|
127
|
+
return {
|
|
128
|
+
...gatedTx,
|
|
129
|
+
result: {
|
|
130
|
+
status: GatedTransactionStatus.DEPENDENCY_FAILED,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const initialCheckResult = await checkGatedTransaction({
|
|
136
|
+
gatedTransaction: gatedTx,
|
|
137
|
+
registry: activityRegistry,
|
|
138
|
+
cachedCheckResult: await cache?.getCachedTxCheckData?.(gatedTx),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await cache?.cacheSuccessfulTxCheckData?.(gatedTx, initialCheckResult.onChainData);
|
|
142
|
+
|
|
143
|
+
if (initialCheckResult.expectationMet) {
|
|
144
|
+
//already met
|
|
145
|
+
return {
|
|
146
|
+
...gatedTx,
|
|
147
|
+
result: {
|
|
148
|
+
status: GatedTransactionStatus.NO_OP,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//get confirmation before sending
|
|
154
|
+
const confirmation = await confirmationCallback(gatedTx);
|
|
155
|
+
if (!confirmation) {
|
|
156
|
+
return {
|
|
157
|
+
...gatedTx,
|
|
158
|
+
result: {
|
|
159
|
+
status: GatedTransactionStatus.DENIED,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//send the tx
|
|
165
|
+
try {
|
|
166
|
+
const processed = await processTx(gatedTx.transaction);
|
|
167
|
+
const finalCheckResult = await checkGatedTransaction({
|
|
168
|
+
registry: activityRegistry,
|
|
169
|
+
gatedTransaction: gatedTx,
|
|
170
|
+
processedTransaction: processed,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
await cache?.cacheSuccessfulTxCheckData?.(gatedTx, finalCheckResult.onChainData);
|
|
174
|
+
|
|
175
|
+
if (finalCheckResult.expectationMet) {
|
|
176
|
+
//sent and successful
|
|
177
|
+
return {
|
|
178
|
+
...gatedTx,
|
|
179
|
+
result: {
|
|
180
|
+
status: GatedTransactionStatus.SUCCESS,
|
|
181
|
+
submittedTransaction: processed,
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
} else {
|
|
185
|
+
//sent and unsuccessful
|
|
186
|
+
return {
|
|
187
|
+
...gatedTx,
|
|
188
|
+
result: {
|
|
189
|
+
status: GatedTransactionStatus.FINAL_CHECK_FAILED,
|
|
190
|
+
submittedTransaction: processed,
|
|
191
|
+
finalOnChainState: finalCheckResult.onChainData,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
} catch (e) {
|
|
196
|
+
//sent and failed
|
|
197
|
+
return {
|
|
198
|
+
...gatedTx,
|
|
199
|
+
result: {
|
|
200
|
+
status: GatedTransactionStatus.TRANSACTION_FAILED,
|
|
201
|
+
transactionError: e,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export const resolveGatedTransactions = async <
|
|
208
|
+
TxType extends Transaction,
|
|
209
|
+
ProcessedTxType extends Transaction,
|
|
210
|
+
GatedTx extends GatedTransaction<string, TxType, any>,
|
|
211
|
+
ProcessTx extends (transaction: TxType) => Promise<ProcessedTxType>,
|
|
212
|
+
>({
|
|
213
|
+
activityRegistry,
|
|
214
|
+
gatedTxes,
|
|
215
|
+
processTx,
|
|
216
|
+
fn,
|
|
217
|
+
cache,
|
|
218
|
+
userInteractionCallbacks,
|
|
219
|
+
}: {
|
|
220
|
+
activityRegistry: ActivityRegistry;
|
|
221
|
+
gatedTxes: GatedTx[];
|
|
222
|
+
processTx: ProcessTx;
|
|
223
|
+
fn: WorkflowFunctions;
|
|
224
|
+
cache?: IGatedTransactionCache;
|
|
225
|
+
userInteractionCallbacks?: UserInteractionCallbacks;
|
|
226
|
+
}): Promise<(GatedTx & { result: GatedTransactionResult })[]> => {
|
|
227
|
+
//compute deps
|
|
228
|
+
const allInternalIds = gatedTxes.map((tx) => getIdForGatedTransaction(tx));
|
|
229
|
+
const allDeps = gatedTxes.flatMap((tx) => tx.dependencies ?? []);
|
|
230
|
+
const allExternalDeps = allDeps.filter(
|
|
231
|
+
(tx) => !allInternalIds.includes(getIdForGatedTransaction(tx)),
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
//prepopulate results for external dependencies
|
|
235
|
+
//this map is only used for internal status tracking
|
|
236
|
+
//map id -> was successful
|
|
237
|
+
const results: Record<GatedTransactionId, boolean> = Object.fromEntries(
|
|
238
|
+
await Promise.all(
|
|
239
|
+
allExternalDeps.map(async (tx) => {
|
|
240
|
+
const checkResult = await checkGatedTransaction({
|
|
241
|
+
gatedTransaction: tx,
|
|
242
|
+
registry: activityRegistry,
|
|
243
|
+
cachedCheckResult: await cache?.getCachedTxCheckData?.(tx),
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
return [getIdForGatedTransaction(tx), checkResult.expectationMet];
|
|
247
|
+
}),
|
|
248
|
+
),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const isTransactionSuccessful = (gtxId: GatedTransactionId) => results[gtxId];
|
|
252
|
+
|
|
253
|
+
const txFunctions = gatedTxes.map((tx) => async () => {
|
|
254
|
+
const res = await resolveGatedTransaction<TxType, ProcessedTxType, GatedTx, ProcessTx>({
|
|
255
|
+
gatedTx: tx,
|
|
256
|
+
activityRegistry,
|
|
257
|
+
isTransactionSuccessful,
|
|
258
|
+
processTx,
|
|
259
|
+
confirmationCallback:
|
|
260
|
+
userInteractionCallbacks?.confirmationCallback ?? (async () => true),
|
|
261
|
+
fn,
|
|
262
|
+
cache,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const resId = getIdForGatedTransaction(res);
|
|
266
|
+
results[resId] = [GatedTransactionStatus.SUCCESS, GatedTransactionStatus.NO_OP].includes(
|
|
267
|
+
res.result.status,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
userInteractionCallbacks?.onResultCallback?.(res);
|
|
271
|
+
return res;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return await Promise.all(txFunctions.map((func) => func()));
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export const resolveSequencedGatedTransactions = async <
|
|
278
|
+
TxType extends Transaction,
|
|
279
|
+
ProcessedTxType extends Transaction,
|
|
280
|
+
GatedTx extends GatedTransaction<string, TxType, any>,
|
|
281
|
+
ProcessTx extends (transaction: TxType) => Promise<ProcessedTxType>,
|
|
282
|
+
>({
|
|
283
|
+
activityRegistry,
|
|
284
|
+
gatedTxes,
|
|
285
|
+
processTx,
|
|
286
|
+
cache,
|
|
287
|
+
userInteractionCallbacks,
|
|
288
|
+
fn,
|
|
289
|
+
}: {
|
|
290
|
+
activityRegistry: ActivityRegistry;
|
|
291
|
+
gatedTxes: GatedTx[][];
|
|
292
|
+
processTx: ProcessTx;
|
|
293
|
+
fn: WorkflowFunctions;
|
|
294
|
+
cache?: IGatedTransactionCache;
|
|
295
|
+
userInteractionCallbacks?: UserInteractionCallbacks;
|
|
296
|
+
}) => {
|
|
297
|
+
const results = [];
|
|
298
|
+
for (const seq of gatedTxes) {
|
|
299
|
+
const result = await resolveGatedTransactions<TxType, ProcessedTxType, GatedTx, ProcessTx>({
|
|
300
|
+
gatedTxes: seq,
|
|
301
|
+
activityRegistry,
|
|
302
|
+
processTx,
|
|
303
|
+
cache,
|
|
304
|
+
userInteractionCallbacks,
|
|
305
|
+
fn,
|
|
306
|
+
});
|
|
307
|
+
results.push(result);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return results;
|
|
311
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import z from 'zod/v4';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
Transaction,
|
|
5
|
+
transactionSchema as _transactionSchema,
|
|
6
|
+
} from '@layerzerolabs/common-chain-model';
|
|
7
|
+
import type { FunctionPointer } from '@layerzerolabs/common-utils';
|
|
8
|
+
import { functionSchema } from '@layerzerolabs/zod-utils';
|
|
9
|
+
|
|
10
|
+
export type OnChainDataComparisonOperator<OnChainDataType> = OnChainDataType extends number
|
|
11
|
+
? '<' | '<=' | '=' | '!=' | '>' | '>='
|
|
12
|
+
: '=' | '!=';
|
|
13
|
+
|
|
14
|
+
export type OnChainDataComparison<OnChainDataType> = {
|
|
15
|
+
operator: OnChainDataComparisonOperator<OnChainDataType>;
|
|
16
|
+
comparisonValue: OnChainDataType;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type GatedTransactionCheck<
|
|
20
|
+
Method extends (...args: any[]) => any = (...args: any[]) => unknown,
|
|
21
|
+
> = {
|
|
22
|
+
functionPointer: FunctionPointer<Method>;
|
|
23
|
+
params: any[];
|
|
24
|
+
expectedResult: OnChainDataComparison<Awaited<ReturnType<Method>>>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const gatedTransactionSchema = <TransactionSchema extends typeof _transactionSchema>({
|
|
28
|
+
transactionSchema,
|
|
29
|
+
name,
|
|
30
|
+
}: {
|
|
31
|
+
transactionSchema: TransactionSchema;
|
|
32
|
+
name: z.ZodString | z.ZodLiteral<string>;
|
|
33
|
+
}) =>
|
|
34
|
+
z.object({
|
|
35
|
+
name,
|
|
36
|
+
transaction: transactionSchema,
|
|
37
|
+
check: z.object({
|
|
38
|
+
functionPointer: z.any(),
|
|
39
|
+
params: z.array(z.any()),
|
|
40
|
+
expectedResult: z.object({
|
|
41
|
+
operator: z.custom<OnChainDataComparisonOperator<any>>(),
|
|
42
|
+
comparisonValue: z.any(),
|
|
43
|
+
}),
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Schema for gated transaction results
|
|
48
|
+
export const gatedTransactionResultSchema = <
|
|
49
|
+
TransactionSchema extends typeof _transactionSchema,
|
|
50
|
+
OnchainDataSchema extends z.ZodType,
|
|
51
|
+
>(
|
|
52
|
+
transactionSchema: TransactionSchema,
|
|
53
|
+
onchainDataSchema: OnchainDataSchema,
|
|
54
|
+
) =>
|
|
55
|
+
z.union([
|
|
56
|
+
z.object({
|
|
57
|
+
status: z.literal(GatedTransactionStatus.NO_OP),
|
|
58
|
+
}),
|
|
59
|
+
z.object({
|
|
60
|
+
status: z.literal(GatedTransactionStatus.SUCCESS),
|
|
61
|
+
submittedTransaction: transactionSchema,
|
|
62
|
+
}),
|
|
63
|
+
z.object({
|
|
64
|
+
status: z.literal(GatedTransactionStatus.TRANSACTION_FAILED),
|
|
65
|
+
transactionError: z.any(),
|
|
66
|
+
}),
|
|
67
|
+
z.object({
|
|
68
|
+
status: z.literal(GatedTransactionStatus.FINAL_CHECK_FAILED),
|
|
69
|
+
submittedTransaction: transactionSchema,
|
|
70
|
+
finalOnChainState: onchainDataSchema,
|
|
71
|
+
}),
|
|
72
|
+
z.object({
|
|
73
|
+
status: z.literal(GatedTransactionStatus.DEPENDENCY_FAILED),
|
|
74
|
+
}),
|
|
75
|
+
z.object({
|
|
76
|
+
status: z.literal(GatedTransactionStatus.DENIED),
|
|
77
|
+
}),
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
// Schema for processed gated transactions (with results)
|
|
81
|
+
export const processedGatedTransactionSchema = <
|
|
82
|
+
TransactionSchema extends typeof _transactionSchema,
|
|
83
|
+
OnchainDataSchema extends z.ZodType,
|
|
84
|
+
>({
|
|
85
|
+
transactionSchema,
|
|
86
|
+
onchainDataSchema,
|
|
87
|
+
name,
|
|
88
|
+
}: {
|
|
89
|
+
transactionSchema: TransactionSchema;
|
|
90
|
+
onchainDataSchema: OnchainDataSchema;
|
|
91
|
+
name: z.ZodString | z.ZodLiteral<string>;
|
|
92
|
+
}) =>
|
|
93
|
+
gatedTransactionSchema({
|
|
94
|
+
transactionSchema,
|
|
95
|
+
name,
|
|
96
|
+
}).and(
|
|
97
|
+
z.object({
|
|
98
|
+
result: gatedTransactionResultSchema(transactionSchema, onchainDataSchema),
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
type GatedTransactionDependencies = GatedTransaction[];
|
|
103
|
+
|
|
104
|
+
export type GatedTransaction<
|
|
105
|
+
Name extends string = string,
|
|
106
|
+
TxType extends Transaction = Transaction,
|
|
107
|
+
CheckMethod extends (...args: any[]) => any = (...args: any[]) => any,
|
|
108
|
+
Dependencies extends GatedTransactionDependencies = GatedTransactionDependencies,
|
|
109
|
+
> = {
|
|
110
|
+
bundleName?: string;
|
|
111
|
+
name: Name;
|
|
112
|
+
transaction: TxType;
|
|
113
|
+
check: GatedTransactionCheck<CheckMethod>;
|
|
114
|
+
cacheable?: boolean;
|
|
115
|
+
uniqueIdKeys?: Record<string, string | number | boolean>;
|
|
116
|
+
dependencies?: Dependencies;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export type ProcessedGatedTransaction<
|
|
120
|
+
Name extends string = string,
|
|
121
|
+
TxType extends Transaction = Transaction,
|
|
122
|
+
CheckMethod extends (...args: any[]) => any = (...args: any[]) => any,
|
|
123
|
+
> = GatedTransaction<Name, TxType, CheckMethod> & {
|
|
124
|
+
result: GatedTransactionResult<GatedTransaction<Name, TxType, CheckMethod>, TxType>;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export type InferOnChainDataTypeFromGatedTransaction<GatedTx extends GatedTransaction> =
|
|
128
|
+
GatedTx extends GatedTransaction<any, any, infer Check> ? Awaited<ReturnType<Check>> : unknown;
|
|
129
|
+
|
|
130
|
+
export enum GatedTransactionStatus {
|
|
131
|
+
NO_OP = 'no_op',
|
|
132
|
+
SUCCESS = 'success',
|
|
133
|
+
TRANSACTION_FAILED = 'transaction_failed',
|
|
134
|
+
FINAL_CHECK_FAILED = 'final_check_failed',
|
|
135
|
+
DEPENDENCY_FAILED = 'dependency_failed',
|
|
136
|
+
DENIED = 'denied',
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
type NoOpGatedTransaction = {
|
|
140
|
+
status: GatedTransactionStatus.NO_OP;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
type SuccessfulGatedTransaction<ProcessedTx extends Transaction> = {
|
|
144
|
+
status: GatedTransactionStatus.SUCCESS;
|
|
145
|
+
submittedTransaction: ProcessedTx;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
type TxFailedGatedTransaction = {
|
|
149
|
+
status: GatedTransactionStatus.TRANSACTION_FAILED;
|
|
150
|
+
transactionError: any;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
type CheckFailedGatedTransaction<ProcessedTx extends Transaction, State> = {
|
|
154
|
+
status: GatedTransactionStatus.FINAL_CHECK_FAILED;
|
|
155
|
+
submittedTransaction: ProcessedTx;
|
|
156
|
+
finalOnChainState: State;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
type DependencyFailedGatedTransaction = {
|
|
160
|
+
status: GatedTransactionStatus.DEPENDENCY_FAILED;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
type DeniedGatedTransaction = {
|
|
164
|
+
status: GatedTransactionStatus.DENIED;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export type GatedTransactionResult<
|
|
168
|
+
GatedTx extends GatedTransaction = GatedTransaction,
|
|
169
|
+
ProcessedTxType extends Transaction = Transaction,
|
|
170
|
+
> =
|
|
171
|
+
| NoOpGatedTransaction
|
|
172
|
+
| SuccessfulGatedTransaction<ProcessedTxType>
|
|
173
|
+
| TxFailedGatedTransaction
|
|
174
|
+
| CheckFailedGatedTransaction<
|
|
175
|
+
ProcessedTxType,
|
|
176
|
+
InferOnChainDataTypeFromGatedTransaction<GatedTx>
|
|
177
|
+
>
|
|
178
|
+
| DependencyFailedGatedTransaction
|
|
179
|
+
| DeniedGatedTransaction;
|
|
180
|
+
|
|
181
|
+
export const gatedTransactionCacheSchema = z.object({
|
|
182
|
+
getCachedTxCheckData: functionSchema({
|
|
183
|
+
input: z.tuple([z.any()]),
|
|
184
|
+
output: z.promise(z.any()),
|
|
185
|
+
}) as z.ZodType<
|
|
186
|
+
<GatedTx extends GatedTransaction>(
|
|
187
|
+
gtx: GatedTx,
|
|
188
|
+
) => Promise<InferOnChainDataTypeFromGatedTransaction<GatedTx> | undefined>
|
|
189
|
+
>,
|
|
190
|
+
cacheSuccessfulTxCheckData: functionSchema({
|
|
191
|
+
input: z.tuple([z.any(), z.any()]),
|
|
192
|
+
output: z.promise(z.any()),
|
|
193
|
+
}) as z.ZodType<
|
|
194
|
+
<GatedTx extends GatedTransaction>(
|
|
195
|
+
gtx: GatedTx,
|
|
196
|
+
data: InferOnChainDataTypeFromGatedTransaction<GatedTx>,
|
|
197
|
+
) => Promise<void>
|
|
198
|
+
>,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
export type IGatedTransactionCache = z.infer<typeof gatedTransactionCacheSchema>;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* name-chain-uniqueIdKeys
|
|
205
|
+
*
|
|
206
|
+
* E.g., setDelegate-ethereum-oAppAddress:0xMY_OAPP
|
|
207
|
+
*/
|
|
208
|
+
export type GatedTransactionId = `${string}-${string}-${string}`;
|
|
209
|
+
|
|
210
|
+
export type UserInteractionCallbacks = {
|
|
211
|
+
confirmationCallback?: (gtx: GatedTransaction) => Promise<boolean>;
|
|
212
|
+
onResultCallback?: (result: GatedTransaction & { result: GatedTransactionResult }) => void;
|
|
213
|
+
};
|