@effectstream/sm 0.100.19 → 0.100.22
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/package.json +10 -11
- package/primitives/Primitive.ts +15 -3
- package/primitives/src/builtin.ts +4 -0
- package/primitives/src/cardano-delayed-asset/delayed-asset-ivm.ts +9 -14
- package/primitives/src/cardano-pool-delegation/pool-delegation-ivm.ts +9 -8
- package/primitives/src/cardano-pool-delegation/pool-delegation-primitive.ts +9 -1
- package/primitives/src/cardano-projected-nft/projected-nft-ivm.ts +9 -15
- package/primitives/src/evm-erc1155/erc1155-ivm.ts +8 -11
- package/primitives/src/evm-erc20/erc20-ivm.ts +19 -21
- package/primitives/src/evm-erc721/erc721-ivm.ts +8 -9
- package/primitives/src/midnight-unshielded-create/midnight-unshielded-create-grammar.ts +8 -0
- package/primitives/src/midnight-unshielded-create/midnight-unshielded-create.ts +121 -0
- package/primitives/src/midnight-zswap-root/midnight-zswap-root-grammar.ts +8 -0
- package/primitives/src/midnight-zswap-root/midnight-zswap-root.ts +121 -0
- package/primitives/src/mod.ts +8 -0
- package/primitives/src/near-intent/near-intent-ivm.ts +6 -11
- package/primitives/src/near-nep141/nep141-ivm.ts +6 -9
- package/primitives/src/near-nep171/nep171-ivm.ts +6 -8
- package/primitives/src/near-nep245/nep245-ivm.ts +6 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effectstream/sm",
|
|
3
|
-
"version": "0.100.
|
|
3
|
+
"version": "0.100.22",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://effectstream.github.io/docs/",
|
|
6
6
|
"repository": {
|
|
@@ -17,21 +17,20 @@
|
|
|
17
17
|
"./grammar": "./primitives/src/grammar.ts"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@effectstream/concise": "0.100.
|
|
21
|
-
"@effectstream/config": "0.100.
|
|
22
|
-
"@effectstream/coroutine": "0.100.
|
|
23
|
-
"@effectstream/crypto": "0.100.
|
|
24
|
-
"@effectstream/db": "0.100.
|
|
25
|
-
"@effectstream/event-client": "0.100.
|
|
26
|
-
"@effectstream/log": "0.100.
|
|
27
|
-
"@effectstream/utils": "0.100.
|
|
28
|
-
"pg": "8.
|
|
20
|
+
"@effectstream/concise": "0.100.22",
|
|
21
|
+
"@effectstream/config": "0.100.22",
|
|
22
|
+
"@effectstream/coroutine": "0.100.22",
|
|
23
|
+
"@effectstream/crypto": "0.100.22",
|
|
24
|
+
"@effectstream/db": "0.100.22",
|
|
25
|
+
"@effectstream/event-client": "0.100.22",
|
|
26
|
+
"@effectstream/log": "0.100.22",
|
|
27
|
+
"@effectstream/utils": "0.100.22",
|
|
28
|
+
"pg": "8.21.0",
|
|
29
29
|
"pg-tx": "^1.0.1",
|
|
30
30
|
"@sinclair/typebox": "0.34.41",
|
|
31
31
|
"assert-never": "^1.4.0",
|
|
32
32
|
"viem": "2.37.3",
|
|
33
33
|
"@pgtyped/runtime": "^2.4.2",
|
|
34
|
-
"@midnight-ntwrk/onchain-runtime": "npm:@midnight-ntwrk/onchain-runtime-v2@2.0.0",
|
|
35
34
|
"effection": "^3.5.0"
|
|
36
35
|
},
|
|
37
36
|
"description": "State machine DSL for EffectStream"
|
package/primitives/Primitive.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
ProtocolPrimitiveMap,
|
|
10
10
|
} from "@effectstream/config";
|
|
11
11
|
import type { AnyPrimitiveType } from "./src/builtin.ts";
|
|
12
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Abstract Class for Effectstream Primitives
|
|
@@ -44,7 +45,15 @@ export abstract class Primitive<
|
|
|
44
45
|
abstract grammar: TGrammar;
|
|
45
46
|
|
|
46
47
|
// Dynamic/ivm Table Global definitions
|
|
47
|
-
|
|
48
|
+
//
|
|
49
|
+
// The `strategy` argument lets the engine pick between an incrementally
|
|
50
|
+
// maintained `pg_ivm` view (when the extension is available) and a plain
|
|
51
|
+
// SQL view fallback (when it isn't). All other DDL — intermediate table,
|
|
52
|
+
// trigger function, trigger — is identical across strategies.
|
|
53
|
+
dynamicTables:
|
|
54
|
+
| undefined
|
|
55
|
+
| ((name: string, strategy: MaterializedViewStrategy) => string) =
|
|
56
|
+
undefined;
|
|
48
57
|
getIntermediatePrefix(): string[] {
|
|
49
58
|
return [];
|
|
50
59
|
}
|
|
@@ -56,8 +65,11 @@ export abstract class Primitive<
|
|
|
56
65
|
abstract getConfig(): ProtocolPrimitiveMap[SyncProtocol];
|
|
57
66
|
|
|
58
67
|
// Arrow function to bind 'this', as this function is passed as a reference
|
|
59
|
-
public getDynamicTables = (
|
|
60
|
-
|
|
68
|
+
public getDynamicTables = (
|
|
69
|
+
name: string,
|
|
70
|
+
strategy: MaterializedViewStrategy,
|
|
71
|
+
): string | undefined => {
|
|
72
|
+
return this.dynamicTables?.(name, strategy);
|
|
61
73
|
};
|
|
62
74
|
|
|
63
75
|
// This returns the payload in the state machine format.
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
export const PrimitiveTypeMidnightGeneric = "Midnight:Generic" as const;
|
|
4
4
|
export const PrimitiveTypeMidnightNullifier = "Midnight:Nullifier" as const;
|
|
5
5
|
export const PrimitiveTypeMidnightUnshieldedSpend = "Midnight:UnshieldedSpend" as const;
|
|
6
|
+
export const PrimitiveTypeMidnightUnshieldedCreate = "Midnight:UnshieldedCreate" as const;
|
|
7
|
+
export const PrimitiveTypeMidnightZswapRoot = "Midnight:ZswapRoot" as const;
|
|
6
8
|
|
|
7
9
|
export const PrimitiveTypeUtxorpcGeneric = "Utxorpc:Generic" as const;
|
|
8
10
|
export const PrimitiveTypeCardanoMintBurn = "Cardano:MintBurn" as const;
|
|
@@ -34,6 +36,8 @@ type BuiltInPrimitives =
|
|
|
34
36
|
typeof PrimitiveTypeMidnightGeneric |
|
|
35
37
|
typeof PrimitiveTypeMidnightNullifier |
|
|
36
38
|
typeof PrimitiveTypeMidnightUnshieldedSpend |
|
|
39
|
+
typeof PrimitiveTypeMidnightUnshieldedCreate |
|
|
40
|
+
typeof PrimitiveTypeMidnightZswapRoot |
|
|
37
41
|
typeof PrimitiveTypeEVMEffectstreamL2 |
|
|
38
42
|
typeof PrimitiveTypeEVMERC721 |
|
|
39
43
|
typeof PrimitiveTypeEVMERC20 |
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { PrimitiveTypeCardanoDelayedAsset } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
|
-
export function delayedAssetIvm(
|
|
4
|
+
export function delayedAssetIvm(
|
|
5
|
+
name: string,
|
|
6
|
+
strategy: MaterializedViewStrategy,
|
|
7
|
+
) {
|
|
4
8
|
const lowerName = name.toLowerCase();
|
|
5
9
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
6
10
|
if (validSQLName !== lowerName) {
|
|
@@ -8,6 +12,9 @@ export function delayedAssetIvm(name: string) {
|
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
const intermediateTable = `primitives.cardano_asset_utxos_intermediate_${validSQLName}`;
|
|
15
|
+
const viewName = `primitives.cardano_asset_utxos_view_${validSQLName}`;
|
|
16
|
+
const selectSql =
|
|
17
|
+
`SELECT primitive_name, tx_id, output_index, address, cip14_fingerprint, policy_id, asset_name, amount, block_height FROM ${intermediateTable}`;
|
|
11
18
|
|
|
12
19
|
return `
|
|
13
20
|
CREATE TABLE IF NOT EXISTS ${intermediateTable} (
|
|
@@ -69,19 +76,7 @@ export function delayedAssetIvm(name: string) {
|
|
|
69
76
|
FOR EACH ROW
|
|
70
77
|
EXECUTE FUNCTION update_delayed_asset_${validSQLName}();
|
|
71
78
|
|
|
72
|
-
|
|
73
|
-
'SELECT
|
|
74
|
-
primitive_name,
|
|
75
|
-
tx_id,
|
|
76
|
-
output_index,
|
|
77
|
-
address,
|
|
78
|
-
cip14_fingerprint,
|
|
79
|
-
policy_id,
|
|
80
|
-
asset_name,
|
|
81
|
-
amount,
|
|
82
|
-
block_height
|
|
83
|
-
FROM ${intermediateTable};
|
|
84
|
-
');
|
|
79
|
+
${strategy.createView(viewName, selectSql)}
|
|
85
80
|
`;
|
|
86
81
|
}
|
|
87
82
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { PrimitiveTypeCardanoPoolDelegation } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
|
-
export function poolDelegationIvm(
|
|
4
|
+
export function poolDelegationIvm(
|
|
5
|
+
name: string,
|
|
6
|
+
strategy: MaterializedViewStrategy,
|
|
7
|
+
) {
|
|
4
8
|
const lowerName = name.toLowerCase();
|
|
5
9
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
6
10
|
if (validSQLName !== lowerName) {
|
|
@@ -8,6 +12,9 @@ export function poolDelegationIvm(name: string) {
|
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
const intermediateTable = `primitives.cardano_pool_delegation_intermediate_${validSQLName}`;
|
|
15
|
+
const viewName = `primitives.cardano_pool_delegation_view_${validSQLName}`;
|
|
16
|
+
const selectSql =
|
|
17
|
+
`SELECT primitive_name, staking_credential, pool_keyhash FROM ${intermediateTable}`;
|
|
11
18
|
|
|
12
19
|
return `
|
|
13
20
|
CREATE TABLE IF NOT EXISTS ${intermediateTable} (
|
|
@@ -51,13 +58,7 @@ export function poolDelegationIvm(name: string) {
|
|
|
51
58
|
FOR EACH ROW
|
|
52
59
|
EXECUTE FUNCTION update_pool_delegation_${validSQLName}();
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
'SELECT
|
|
56
|
-
primitive_name,
|
|
57
|
-
staking_credential,
|
|
58
|
-
pool_keyhash
|
|
59
|
-
FROM ${intermediateTable};
|
|
60
|
-
');
|
|
61
|
+
${strategy.createView(viewName, selectSql)}
|
|
61
62
|
`;
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -85,7 +85,15 @@ export class CardanoPoolDelegationPrimitive extends Primitive<
|
|
|
85
85
|
type: this.internalTypeName,
|
|
86
86
|
startBlockHeight: this.startBlockHeight,
|
|
87
87
|
scheduledPrefix: this.stateMachinePrefix,
|
|
88
|
-
predicate: {
|
|
88
|
+
predicate: {
|
|
89
|
+
match: {
|
|
90
|
+
cardano: {
|
|
91
|
+
has_certificate: this.pools.length === 1
|
|
92
|
+
? { any_pool_keyhash: this.pools[0] }
|
|
93
|
+
: true,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
89
97
|
} as const;
|
|
90
98
|
}
|
|
91
99
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { PrimitiveTypeCardanoProjectedNFT } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
|
-
export function projectedNftIvm(
|
|
4
|
+
export function projectedNftIvm(
|
|
5
|
+
name: string,
|
|
6
|
+
strategy: MaterializedViewStrategy,
|
|
7
|
+
) {
|
|
4
8
|
const lowerName = name.toLowerCase();
|
|
5
9
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
6
10
|
if (validSQLName !== lowerName) {
|
|
@@ -8,6 +12,9 @@ export function projectedNftIvm(name: string) {
|
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
const intermediateTable = `primitives.cardano_projected_nft_intermediate_${validSQLName}`;
|
|
15
|
+
const viewName = `primitives.cardano_projected_nft_view_${validSQLName}`;
|
|
16
|
+
const selectSql =
|
|
17
|
+
`SELECT primitive_name, owner_address, current_tx_id, current_output_index, previous_tx_id, previous_output_index, policy_id, asset_name, status, for_how_long FROM ${intermediateTable}`;
|
|
11
18
|
|
|
12
19
|
return `
|
|
13
20
|
CREATE TABLE IF NOT EXISTS ${intermediateTable} (
|
|
@@ -87,20 +94,7 @@ export function projectedNftIvm(name: string) {
|
|
|
87
94
|
FOR EACH ROW
|
|
88
95
|
EXECUTE FUNCTION update_projected_nft_${validSQLName}();
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
'SELECT
|
|
92
|
-
primitive_name,
|
|
93
|
-
owner_address,
|
|
94
|
-
current_tx_id,
|
|
95
|
-
current_output_index,
|
|
96
|
-
previous_tx_id,
|
|
97
|
-
previous_output_index,
|
|
98
|
-
policy_id,
|
|
99
|
-
asset_name,
|
|
100
|
-
status,
|
|
101
|
-
for_how_long
|
|
102
|
-
FROM ${intermediateTable};
|
|
103
|
-
');
|
|
97
|
+
${strategy.createView(viewName, selectSql)}
|
|
104
98
|
`;
|
|
105
99
|
}
|
|
106
100
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { PrimitiveTypeEVMERC1155 } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Creates a new IVM for the ERC1155 token with the given name.
|
|
5
6
|
* This will track the current owner of each token.
|
|
6
7
|
* @param name - The name of the ERC1155 token.
|
|
8
|
+
* @param strategy - How to publish the read-side view (pg_ivm or plain VIEW).
|
|
7
9
|
* @returns A string containing the SQL script to create the IVM.
|
|
8
10
|
*/
|
|
9
|
-
export function erc1155Ivm(name: string) {
|
|
11
|
+
export function erc1155Ivm(name: string, strategy: MaterializedViewStrategy) {
|
|
10
12
|
const lowerName = name.toLowerCase();
|
|
11
13
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
12
14
|
if (validSQLName !== lowerName) {
|
|
@@ -15,6 +17,9 @@ export function erc1155Ivm(name: string) {
|
|
|
15
17
|
|
|
16
18
|
const ownershipTable =
|
|
17
19
|
`primitives.erc1155_ownership_intermediate_${validSQLName}`;
|
|
20
|
+
const viewName = `primitives.erc1155_ownership_view_${validSQLName}`;
|
|
21
|
+
const selectSql =
|
|
22
|
+
`SELECT primitive_name, token_id, owner_address, amount FROM ${ownershipTable} WHERE amount > 0`;
|
|
18
23
|
|
|
19
24
|
return `
|
|
20
25
|
-- Intermediate table for current ERC1155 ownership (maintained by triggers)
|
|
@@ -85,16 +90,8 @@ export function erc1155Ivm(name: string) {
|
|
|
85
90
|
FOR EACH ROW
|
|
86
91
|
EXECUTE FUNCTION update_erc1155_ownership_${validSQLName}();
|
|
87
92
|
|
|
88
|
-
--
|
|
89
|
-
|
|
90
|
-
'SELECT
|
|
91
|
-
primitive_name,
|
|
92
|
-
token_id,
|
|
93
|
-
owner_address,
|
|
94
|
-
amount
|
|
95
|
-
FROM ${ownershipTable}
|
|
96
|
-
WHERE amount > 0;
|
|
97
|
-
');
|
|
93
|
+
-- Publish the read-side view (incrementally maintained or plain, per strategy)
|
|
94
|
+
${strategy.createView(viewName, selectSql)}
|
|
98
95
|
`;
|
|
99
96
|
}
|
|
100
97
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// This will allow to create new tables during migrations.
|
|
3
3
|
|
|
4
4
|
import { PrimitiveTypeEVMERC20 } from "../builtin.ts";
|
|
5
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
5
6
|
|
|
6
7
|
// TODO The IVM here is not relevant, as the trigger does the work.
|
|
7
8
|
|
|
@@ -9,9 +10,10 @@ import { PrimitiveTypeEVMERC20 } from "../builtin.ts";
|
|
|
9
10
|
* Creates a new IVM for the ERC20 token with the given name.
|
|
10
11
|
* This will track the current balance of each address.
|
|
11
12
|
* @param name - The name of the ERC20 token.
|
|
13
|
+
* @param strategy - How to publish the read-side view (pg_ivm or plain VIEW).
|
|
12
14
|
* @returns A string containing the SQL script to create the IVM.
|
|
13
15
|
*/
|
|
14
|
-
export function erc20Ivm(name: string) {
|
|
16
|
+
export function erc20Ivm(name: string, strategy: MaterializedViewStrategy) {
|
|
15
17
|
const lowerName = name.toLowerCase();
|
|
16
18
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
17
19
|
if (validSQLName !== lowerName) {
|
|
@@ -19,6 +21,9 @@ export function erc20Ivm(name: string) {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
const balanceTable = `primitives.erc20_balances_intermediate_${validSQLName}`;
|
|
24
|
+
const viewName = `primitives.erc20_balances_view_${validSQLName}`;
|
|
25
|
+
const selectSql =
|
|
26
|
+
`SELECT primitive_name, address, balance FROM ${balanceTable} WHERE balance > 0`;
|
|
22
27
|
|
|
23
28
|
return `
|
|
24
29
|
-- Intermediate table for current ERC20 balances (maintained by triggers)
|
|
@@ -28,7 +33,7 @@ export function erc20Ivm(name: string) {
|
|
|
28
33
|
balance numeric(78,0) DEFAULT 0,
|
|
29
34
|
PRIMARY KEY (primitive_name, address)
|
|
30
35
|
);
|
|
31
|
-
|
|
36
|
+
|
|
32
37
|
-- Trigger function to maintain ERC20 balances
|
|
33
38
|
CREATE OR REPLACE FUNCTION update_erc20_balances_${validSQLName}() RETURNS TRIGGER AS $$
|
|
34
39
|
DECLARE
|
|
@@ -37,25 +42,25 @@ export function erc20Ivm(name: string) {
|
|
|
37
42
|
to_address TEXT;
|
|
38
43
|
BEGIN
|
|
39
44
|
-- Only process ERC20 transfers
|
|
40
|
-
IF NEW.payload_type = '${PrimitiveTypeEVMERC20}'
|
|
45
|
+
IF NEW.payload_type = '${PrimitiveTypeEVMERC20}'
|
|
41
46
|
AND NEW.primitive_name = '${name}'
|
|
42
47
|
AND NEW.payload->>'value' IS NOT NULL THEN
|
|
43
|
-
|
|
48
|
+
|
|
44
49
|
transfer_value := (NEW.payload->>'value')::numeric(78,0);
|
|
45
50
|
from_address := lower(NEW.payload->>'from');
|
|
46
51
|
to_address := lower(NEW.payload->>'to');
|
|
47
|
-
|
|
52
|
+
|
|
48
53
|
-- Handle FROM address (subtract balance, but skip burn address)
|
|
49
|
-
IF from_address IS NOT NULL
|
|
50
|
-
AND from_address != ''
|
|
54
|
+
IF from_address IS NOT NULL
|
|
55
|
+
AND from_address != ''
|
|
51
56
|
AND from_address != '0x0000000000000000000000000000000000000000' THEN
|
|
52
|
-
|
|
57
|
+
|
|
53
58
|
INSERT INTO ${balanceTable} (primitive_name, address, balance)
|
|
54
59
|
VALUES (NEW.primitive_name, from_address, -transfer_value)
|
|
55
60
|
ON CONFLICT (primitive_name, address) DO UPDATE SET
|
|
56
61
|
balance = ${balanceTable}.balance - transfer_value;
|
|
57
62
|
END IF;
|
|
58
|
-
|
|
63
|
+
|
|
59
64
|
-- Handle TO address (add balance)
|
|
60
65
|
IF to_address IS NOT NULL AND to_address != '' THEN
|
|
61
66
|
INSERT INTO ${balanceTable} (primitive_name, address, balance)
|
|
@@ -64,26 +69,19 @@ export function erc20Ivm(name: string) {
|
|
|
64
69
|
balance = ${balanceTable}.balance + transfer_value;
|
|
65
70
|
END IF;
|
|
66
71
|
END IF;
|
|
67
|
-
|
|
72
|
+
|
|
68
73
|
RETURN NEW;
|
|
69
74
|
END;
|
|
70
75
|
$$ LANGUAGE plpgsql;
|
|
71
|
-
|
|
76
|
+
|
|
72
77
|
-- Create trigger on primitive_accounting for ERC20 balances
|
|
73
78
|
CREATE TRIGGER trigger_update_erc20_balances_${validSQLName}
|
|
74
79
|
AFTER INSERT ON effectstream.primitive_accounting -- Note: Qualify with paima schema
|
|
75
80
|
FOR EACH ROW
|
|
76
81
|
EXECUTE FUNCTION update_erc20_balances_${validSQLName}();
|
|
77
|
-
|
|
78
|
-
--
|
|
79
|
-
|
|
80
|
-
'SELECT
|
|
81
|
-
primitive_name,
|
|
82
|
-
address,
|
|
83
|
-
balance
|
|
84
|
-
FROM ${balanceTable}
|
|
85
|
-
WHERE balance > 0;
|
|
86
|
-
');
|
|
82
|
+
|
|
83
|
+
-- Publish the read-side view (incrementally maintained or plain, per strategy)
|
|
84
|
+
${strategy.createView(viewName, selectSql)}
|
|
87
85
|
`;
|
|
88
86
|
}
|
|
89
87
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { PrimitiveTypeEVMERC721 } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Creates a new IVM for the ERC721 token with the given name.
|
|
5
6
|
* This will track the current owner of each token.
|
|
6
7
|
* @param name - The name of the ERC721 token.
|
|
8
|
+
* @param strategy - How to publish the read-side view (pg_ivm or plain VIEW).
|
|
7
9
|
* @returns A string containing the SQL script to create the IVM.
|
|
8
10
|
*/
|
|
9
|
-
export function erc721Ivm(name: string) {
|
|
11
|
+
export function erc721Ivm(name: string, strategy: MaterializedViewStrategy) {
|
|
10
12
|
const lowerName = name.toLowerCase();
|
|
11
13
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
12
14
|
if (validSQLName !== lowerName) {
|
|
@@ -15,6 +17,9 @@ export function erc721Ivm(name: string) {
|
|
|
15
17
|
|
|
16
18
|
const ownershipTable =
|
|
17
19
|
`primitives.erc721_ownership_intermediate_${validSQLName}`;
|
|
20
|
+
const viewName = `primitives.erc721_ownership_view_${validSQLName}`;
|
|
21
|
+
const selectSql =
|
|
22
|
+
`SELECT primitive_name, token_id, current_owner FROM ${ownershipTable}`;
|
|
18
23
|
|
|
19
24
|
return `
|
|
20
25
|
-- Intermediate table for current ERC721 ownership (maintained by triggers)
|
|
@@ -68,14 +73,8 @@ export function erc721Ivm(name: string) {
|
|
|
68
73
|
FOR EACH ROW
|
|
69
74
|
EXECUTE FUNCTION update_erc721_ownership_${validSQLName}();
|
|
70
75
|
|
|
71
|
-
--
|
|
72
|
-
|
|
73
|
-
'SELECT
|
|
74
|
-
primitive_name,
|
|
75
|
-
token_id,
|
|
76
|
-
current_owner
|
|
77
|
-
FROM ${ownershipTable};
|
|
78
|
-
');
|
|
76
|
+
-- Publish the read-side view (incrementally maintained or plain, per strategy)
|
|
77
|
+
${strategy.createView(viewName, selectSql)}
|
|
79
78
|
`;
|
|
80
79
|
}
|
|
81
80
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Primitive } from "@effectstream/sm";
|
|
2
|
+
import {
|
|
3
|
+
type AddressAndType,
|
|
4
|
+
AddressType,
|
|
5
|
+
type EffectstreamBlockNumber,
|
|
6
|
+
} from "@effectstream/utils";
|
|
7
|
+
import type { StaticDecode } from "@sinclair/typebox";
|
|
8
|
+
import {
|
|
9
|
+
type CommandTuple,
|
|
10
|
+
generateRawStmInput,
|
|
11
|
+
type ParamToData,
|
|
12
|
+
} from "@effectstream/concise";
|
|
13
|
+
import type {
|
|
14
|
+
ConfigSyncProtocolType,
|
|
15
|
+
FlattenSyncProtocolIOFor,
|
|
16
|
+
ProtocolPrimitiveMap,
|
|
17
|
+
} from "@effectstream/config";
|
|
18
|
+
import type { SyncStateUpdateStream } from "@effectstream/coroutine";
|
|
19
|
+
import { PrimitiveTypeMidnightUnshieldedCreate } from "../builtin.ts";
|
|
20
|
+
import { midnightUnshieldedCreateGrammar } from "./midnight-unshielded-create-grammar.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* MidnightUnshieldedCreatePrimitive
|
|
24
|
+
*
|
|
25
|
+
* Watches all unshielded UTXO *creation* events on the Midnight ledger — the
|
|
26
|
+
* mirror of MidnightUnshieldedSpendPrimitive. The indexer's GraphQL
|
|
27
|
+
* `transactions.unshieldedCreatedOutputs` field reports the
|
|
28
|
+
* `(owner, intentHash, outputIndex)` triple for each unshielded UTXO created
|
|
29
|
+
* in a block (by regular AND system transactions — rewards/bridge mint
|
|
30
|
+
* unshielded UTXOs). One state-machine input is emitted per created output.
|
|
31
|
+
*
|
|
32
|
+
* Lets a node maintain a persistent set of UTXOs that have actually been
|
|
33
|
+
* created on chain, so an offer referencing a never-created unshielded UTXO
|
|
34
|
+
* can be rejected (existence check).
|
|
35
|
+
*
|
|
36
|
+
* Usage:
|
|
37
|
+
* stm.addStateTransition("myPrefix", function* (data) {
|
|
38
|
+
* const { payload } = data.parsedInput;
|
|
39
|
+
* // payload = { owner, intentHash, outputIndex, txHash }
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
export class MidnightUnshieldedCreatePrimitive extends Primitive<
|
|
43
|
+
ConfigSyncProtocolType.MIDNIGHT_PARALLEL,
|
|
44
|
+
typeof midnightUnshieldedCreateGrammar
|
|
45
|
+
> {
|
|
46
|
+
readonly internalTypeName = PrimitiveTypeMidnightUnshieldedCreate;
|
|
47
|
+
override readonly grammar = midnightUnshieldedCreateGrammar;
|
|
48
|
+
|
|
49
|
+
override dynamicTables = undefined;
|
|
50
|
+
override getIntermediatePrefix(): string[] {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
override getViewPrefix(): string[] {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
constructor(config: {
|
|
58
|
+
instanceName: string;
|
|
59
|
+
startBlockHeight: number;
|
|
60
|
+
stateMachinePrefix: string;
|
|
61
|
+
}) {
|
|
62
|
+
super(config);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override *getPayload(
|
|
66
|
+
_: EffectstreamBlockNumber,
|
|
67
|
+
primitiveTransactionData: FlattenSyncProtocolIOFor<
|
|
68
|
+
ConfigSyncProtocolType.MIDNIGHT_PARALLEL
|
|
69
|
+
>,
|
|
70
|
+
): SyncStateUpdateStream<{
|
|
71
|
+
isBatched: boolean;
|
|
72
|
+
data: {
|
|
73
|
+
fromAddressAndType: AddressAndType;
|
|
74
|
+
stateMachinePayload:
|
|
75
|
+
| StaticDecode<CommandTuple<string, typeof midnightUnshieldedCreateGrammar>>
|
|
76
|
+
| null;
|
|
77
|
+
accountingPayload: ParamToData<typeof midnightUnshieldedCreateGrammar>;
|
|
78
|
+
}[];
|
|
79
|
+
}> {
|
|
80
|
+
const payload = primitiveTransactionData.output.payload;
|
|
81
|
+
|
|
82
|
+
const accountingPayload: ParamToData<typeof this.grammar> = {
|
|
83
|
+
payload,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const stateMachinePayload:
|
|
87
|
+
| StaticDecode<CommandTuple<string, typeof this.grammar>>
|
|
88
|
+
| null = this.stateMachinePrefix
|
|
89
|
+
? generateRawStmInput(
|
|
90
|
+
this.grammar,
|
|
91
|
+
this.stateMachinePrefix,
|
|
92
|
+
accountingPayload,
|
|
93
|
+
)
|
|
94
|
+
: null;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
isBatched: false,
|
|
98
|
+
data: [
|
|
99
|
+
{
|
|
100
|
+
fromAddressAndType: {
|
|
101
|
+
type: AddressType.NONE,
|
|
102
|
+
address: "0x0",
|
|
103
|
+
},
|
|
104
|
+
accountingPayload,
|
|
105
|
+
stateMachinePayload,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
override getConfig(): ProtocolPrimitiveMap[
|
|
112
|
+
ConfigSyncProtocolType.MIDNIGHT_PARALLEL
|
|
113
|
+
] {
|
|
114
|
+
return {
|
|
115
|
+
name: this.instanceName,
|
|
116
|
+
type: this.internalTypeName,
|
|
117
|
+
startBlockHeight: this.startBlockHeight,
|
|
118
|
+
scheduledPrefix: this.stateMachinePrefix ?? "",
|
|
119
|
+
} as const;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Primitive } from "@effectstream/sm";
|
|
2
|
+
import {
|
|
3
|
+
type AddressAndType,
|
|
4
|
+
AddressType,
|
|
5
|
+
type EffectstreamBlockNumber,
|
|
6
|
+
} from "@effectstream/utils";
|
|
7
|
+
import type { StaticDecode } from "@sinclair/typebox";
|
|
8
|
+
import {
|
|
9
|
+
type CommandTuple,
|
|
10
|
+
generateRawStmInput,
|
|
11
|
+
type ParamToData,
|
|
12
|
+
} from "@effectstream/concise";
|
|
13
|
+
import type {
|
|
14
|
+
ConfigSyncProtocolType,
|
|
15
|
+
FlattenSyncProtocolIOFor,
|
|
16
|
+
ProtocolPrimitiveMap,
|
|
17
|
+
} from "@effectstream/config";
|
|
18
|
+
import type { SyncStateUpdateStream } from "@effectstream/coroutine";
|
|
19
|
+
import { PrimitiveTypeMidnightZswapRoot } from "../builtin.ts";
|
|
20
|
+
import { midnightZswapRootGrammar } from "./midnight-zswap-root-grammar.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* MidnightZswapRootPrimitive
|
|
24
|
+
*
|
|
25
|
+
* Emits the zswap coin-commitment Merkle tree root as it advances on chain.
|
|
26
|
+
* The indexer's `RegularTransaction.zswapMerkleTreeRoot` is the tree root
|
|
27
|
+
* *after* that transaction; this primitive surfaces the latest such root per
|
|
28
|
+
* block (the last regular transaction's root), with the block timestamp.
|
|
29
|
+
*
|
|
30
|
+
* A node can accumulate these into a windowed "known roots" set (mirroring the
|
|
31
|
+
* ledger's `past_roots`, retained for the on-chain root window) and reject an
|
|
32
|
+
* offer that proves against a root the chain never held or that has aged out —
|
|
33
|
+
* the root-known liveness check. Blocks with no regular transactions emit
|
|
34
|
+
* nothing (the root is unchanged).
|
|
35
|
+
*
|
|
36
|
+
* Usage:
|
|
37
|
+
* stm.addStateTransition("myPrefix", function* (data) {
|
|
38
|
+
* const { payload } = data.parsedInput;
|
|
39
|
+
* // payload = { root, txHash } ; data.blockTimestamp is the window key
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
export class MidnightZswapRootPrimitive extends Primitive<
|
|
43
|
+
ConfigSyncProtocolType.MIDNIGHT_PARALLEL,
|
|
44
|
+
typeof midnightZswapRootGrammar
|
|
45
|
+
> {
|
|
46
|
+
readonly internalTypeName = PrimitiveTypeMidnightZswapRoot;
|
|
47
|
+
override readonly grammar = midnightZswapRootGrammar;
|
|
48
|
+
|
|
49
|
+
override dynamicTables = undefined;
|
|
50
|
+
override getIntermediatePrefix(): string[] {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
override getViewPrefix(): string[] {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
constructor(config: {
|
|
58
|
+
instanceName: string;
|
|
59
|
+
startBlockHeight: number;
|
|
60
|
+
stateMachinePrefix: string;
|
|
61
|
+
}) {
|
|
62
|
+
super(config);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override *getPayload(
|
|
66
|
+
_: EffectstreamBlockNumber,
|
|
67
|
+
primitiveTransactionData: FlattenSyncProtocolIOFor<
|
|
68
|
+
ConfigSyncProtocolType.MIDNIGHT_PARALLEL
|
|
69
|
+
>,
|
|
70
|
+
): SyncStateUpdateStream<{
|
|
71
|
+
isBatched: boolean;
|
|
72
|
+
data: {
|
|
73
|
+
fromAddressAndType: AddressAndType;
|
|
74
|
+
stateMachinePayload:
|
|
75
|
+
| StaticDecode<CommandTuple<string, typeof midnightZswapRootGrammar>>
|
|
76
|
+
| null;
|
|
77
|
+
accountingPayload: ParamToData<typeof midnightZswapRootGrammar>;
|
|
78
|
+
}[];
|
|
79
|
+
}> {
|
|
80
|
+
const payload = primitiveTransactionData.output.payload;
|
|
81
|
+
|
|
82
|
+
const accountingPayload: ParamToData<typeof this.grammar> = {
|
|
83
|
+
payload,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const stateMachinePayload:
|
|
87
|
+
| StaticDecode<CommandTuple<string, typeof this.grammar>>
|
|
88
|
+
| null = this.stateMachinePrefix
|
|
89
|
+
? generateRawStmInput(
|
|
90
|
+
this.grammar,
|
|
91
|
+
this.stateMachinePrefix,
|
|
92
|
+
accountingPayload,
|
|
93
|
+
)
|
|
94
|
+
: null;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
isBatched: false,
|
|
98
|
+
data: [
|
|
99
|
+
{
|
|
100
|
+
fromAddressAndType: {
|
|
101
|
+
type: AddressType.NONE,
|
|
102
|
+
address: "0x0",
|
|
103
|
+
},
|
|
104
|
+
accountingPayload,
|
|
105
|
+
stateMachinePayload,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
override getConfig(): ProtocolPrimitiveMap[
|
|
112
|
+
ConfigSyncProtocolType.MIDNIGHT_PARALLEL
|
|
113
|
+
] {
|
|
114
|
+
return {
|
|
115
|
+
name: this.instanceName,
|
|
116
|
+
type: this.internalTypeName,
|
|
117
|
+
startBlockHeight: this.startBlockHeight,
|
|
118
|
+
scheduledPrefix: this.stateMachinePrefix ?? "",
|
|
119
|
+
} as const;
|
|
120
|
+
}
|
|
121
|
+
}
|
package/primitives/src/mod.ts
CHANGED
|
@@ -2,6 +2,8 @@ import {
|
|
|
2
2
|
PrimitiveTypeMidnightGeneric,
|
|
3
3
|
PrimitiveTypeMidnightNullifier,
|
|
4
4
|
PrimitiveTypeMidnightUnshieldedSpend,
|
|
5
|
+
PrimitiveTypeMidnightUnshieldedCreate,
|
|
6
|
+
PrimitiveTypeMidnightZswapRoot,
|
|
5
7
|
PrimitiveTypeEVMEffectstreamL2,
|
|
6
8
|
PrimitiveTypeEVMERC721,
|
|
7
9
|
PrimitiveTypeEVMERC20,
|
|
@@ -27,6 +29,8 @@ import {
|
|
|
27
29
|
import { MidnightGenericPrimitive } from "./midnight-generic/midnight-genetic.ts";
|
|
28
30
|
import { MidnightNullifierPrimitive } from "./midnight-nullifier/midnight-nullifier.ts";
|
|
29
31
|
import { MidnightUnshieldedSpendPrimitive } from "./midnight-unshielded-spend/midnight-unshielded-spend.ts";
|
|
32
|
+
import { MidnightUnshieldedCreatePrimitive } from "./midnight-unshielded-create/midnight-unshielded-create.ts";
|
|
33
|
+
import { MidnightZswapRootPrimitive } from "./midnight-zswap-root/midnight-zswap-root.ts";
|
|
30
34
|
import { EffectstreamL2Primitive } from "./evm-effectstream-l2/effectstream-l2-primitive.ts";
|
|
31
35
|
import { Erc721Primitive } from "./evm-erc721/erc721-primitive.ts";
|
|
32
36
|
import { Erc20Primitive } from "./evm-erc20/erc20-primitive.ts";
|
|
@@ -52,6 +56,8 @@ const builtInPrimitivesMap = {
|
|
|
52
56
|
[PrimitiveTypeMidnightGeneric]: MidnightGenericPrimitive,
|
|
53
57
|
[PrimitiveTypeMidnightNullifier]: MidnightNullifierPrimitive,
|
|
54
58
|
[PrimitiveTypeMidnightUnshieldedSpend]: MidnightUnshieldedSpendPrimitive,
|
|
59
|
+
[PrimitiveTypeMidnightUnshieldedCreate]: MidnightUnshieldedCreatePrimitive,
|
|
60
|
+
[PrimitiveTypeMidnightZswapRoot]: MidnightZswapRootPrimitive,
|
|
55
61
|
[PrimitiveTypeEVMEffectstreamL2]: EffectstreamL2Primitive,
|
|
56
62
|
[PrimitiveTypeEVMERC721]: Erc721Primitive,
|
|
57
63
|
[PrimitiveTypeEVMERC20]: Erc20Primitive,
|
|
@@ -82,6 +88,8 @@ export {
|
|
|
82
88
|
MidnightGenericPrimitive,
|
|
83
89
|
MidnightNullifierPrimitive,
|
|
84
90
|
MidnightUnshieldedSpendPrimitive,
|
|
91
|
+
MidnightUnshieldedCreatePrimitive,
|
|
92
|
+
MidnightZswapRootPrimitive,
|
|
85
93
|
EffectstreamL2Primitive,
|
|
86
94
|
Erc721Primitive,
|
|
87
95
|
Erc20Primitive,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PrimitiveTypeNEARIntent } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
|
-
export function nearIntentIvm(name: string) {
|
|
4
|
+
export function nearIntentIvm(name: string, strategy: MaterializedViewStrategy) {
|
|
4
5
|
const lowerName = name.toLowerCase();
|
|
5
6
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
6
7
|
if (validSQLName !== lowerName) {
|
|
@@ -8,6 +9,9 @@ export function nearIntentIvm(name: string) {
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const settlementTable = `primitives.near_intent_settlement_intermediate_${validSQLName}`;
|
|
12
|
+
const viewName = `primitives.near_intent_settlement_view_${validSQLName}`;
|
|
13
|
+
const selectSql =
|
|
14
|
+
`SELECT primitive_name, intent_hash, account_id, token_id, amount, block_height FROM ${settlementTable}`;
|
|
11
15
|
|
|
12
16
|
return `
|
|
13
17
|
CREATE TABLE IF NOT EXISTS ${settlementTable} (
|
|
@@ -54,16 +58,7 @@ export function nearIntentIvm(name: string) {
|
|
|
54
58
|
FOR EACH ROW
|
|
55
59
|
EXECUTE FUNCTION update_near_intent_settlement_${validSQLName}();
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
'SELECT
|
|
59
|
-
primitive_name,
|
|
60
|
-
intent_hash,
|
|
61
|
-
account_id,
|
|
62
|
-
token_id,
|
|
63
|
-
amount,
|
|
64
|
-
block_height
|
|
65
|
-
FROM ${settlementTable};
|
|
66
|
-
');
|
|
61
|
+
${strategy.createView(viewName, selectSql)}
|
|
67
62
|
`;
|
|
68
63
|
}
|
|
69
64
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PrimitiveTypeNEARNEP141 } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
|
-
export function nep141Ivm(name: string) {
|
|
4
|
+
export function nep141Ivm(name: string, strategy: MaterializedViewStrategy) {
|
|
4
5
|
const lowerName = name.toLowerCase();
|
|
5
6
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
6
7
|
if (validSQLName !== lowerName) {
|
|
@@ -8,6 +9,9 @@ export function nep141Ivm(name: string) {
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const balanceTable = `primitives.nep141_balance_intermediate_${validSQLName}`;
|
|
12
|
+
const viewName = `primitives.nep141_balance_view_${validSQLName}`;
|
|
13
|
+
const selectSql =
|
|
14
|
+
`SELECT primitive_name, account_id, balance FROM ${balanceTable} WHERE balance > 0`;
|
|
11
15
|
|
|
12
16
|
return `
|
|
13
17
|
CREATE TABLE IF NOT EXISTS ${balanceTable} (
|
|
@@ -55,14 +59,7 @@ export function nep141Ivm(name: string) {
|
|
|
55
59
|
FOR EACH ROW
|
|
56
60
|
EXECUTE FUNCTION update_nep141_balance_${validSQLName}();
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
'SELECT
|
|
60
|
-
primitive_name,
|
|
61
|
-
account_id,
|
|
62
|
-
balance
|
|
63
|
-
FROM ${balanceTable}
|
|
64
|
-
WHERE balance > 0;
|
|
65
|
-
');
|
|
62
|
+
${strategy.createView(viewName, selectSql)}
|
|
66
63
|
`;
|
|
67
64
|
}
|
|
68
65
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PrimitiveTypeNEARNEP171 } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
|
-
export function nep171Ivm(name: string) {
|
|
4
|
+
export function nep171Ivm(name: string, strategy: MaterializedViewStrategy) {
|
|
4
5
|
const lowerName = name.toLowerCase();
|
|
5
6
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
6
7
|
if (validSQLName !== lowerName) {
|
|
@@ -8,6 +9,9 @@ export function nep171Ivm(name: string) {
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const ownerTable = `primitives.nep171_owner_intermediate_${validSQLName}`;
|
|
12
|
+
const viewName = `primitives.nep171_owner_view_${validSQLName}`;
|
|
13
|
+
const selectSql =
|
|
14
|
+
`SELECT primitive_name, token_id, current_owner FROM ${ownerTable}`;
|
|
11
15
|
|
|
12
16
|
return `
|
|
13
17
|
CREATE TABLE IF NOT EXISTS ${ownerTable} (
|
|
@@ -57,13 +61,7 @@ export function nep171Ivm(name: string) {
|
|
|
57
61
|
FOR EACH ROW
|
|
58
62
|
EXECUTE FUNCTION update_nep171_owner_${validSQLName}();
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
'SELECT
|
|
62
|
-
primitive_name,
|
|
63
|
-
token_id,
|
|
64
|
-
current_owner
|
|
65
|
-
FROM ${ownerTable};
|
|
66
|
-
');
|
|
64
|
+
${strategy.createView(viewName, selectSql)}
|
|
67
65
|
`;
|
|
68
66
|
}
|
|
69
67
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PrimitiveTypeNEARNEP245 } from "../builtin.ts";
|
|
2
|
+
import type { MaterializedViewStrategy } from "@effectstream/db";
|
|
2
3
|
|
|
3
|
-
export function nep245Ivm(name: string) {
|
|
4
|
+
export function nep245Ivm(name: string, strategy: MaterializedViewStrategy) {
|
|
4
5
|
const lowerName = name.toLowerCase();
|
|
5
6
|
const validSQLName = lowerName.replace(/[^a-zA-Z0-9_]/g, "");
|
|
6
7
|
if (validSQLName !== lowerName) {
|
|
@@ -8,6 +9,9 @@ export function nep245Ivm(name: string) {
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const balanceTable = `primitives.nep245_balance_intermediate_${validSQLName}`;
|
|
12
|
+
const viewName = `primitives.nep245_balance_view_${validSQLName}`;
|
|
13
|
+
const selectSql =
|
|
14
|
+
`SELECT primitive_name, token_id, account_id, amount FROM ${balanceTable} WHERE amount > 0`;
|
|
11
15
|
|
|
12
16
|
return `
|
|
13
17
|
CREATE TABLE IF NOT EXISTS ${balanceTable} (
|
|
@@ -68,15 +72,7 @@ export function nep245Ivm(name: string) {
|
|
|
68
72
|
FOR EACH ROW
|
|
69
73
|
EXECUTE FUNCTION update_nep245_balance_${validSQLName}();
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
'SELECT
|
|
73
|
-
primitive_name,
|
|
74
|
-
token_id,
|
|
75
|
-
account_id,
|
|
76
|
-
amount
|
|
77
|
-
FROM ${balanceTable}
|
|
78
|
-
WHERE amount > 0;
|
|
79
|
-
');
|
|
75
|
+
${strategy.createView(viewName, selectSql)}
|
|
80
76
|
`;
|
|
81
77
|
}
|
|
82
78
|
|