otomato-sdk 2.0.71 → 2.0.72

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.
@@ -2727,11 +2727,12 @@ export const ACTIONS = {
2727
2727
  },
2728
2728
  {
2729
2729
  "key": "amount",
2730
- "type": "uint256",
2730
+ "type": "float",
2731
2731
  "description": "Amount to sell",
2732
2732
  "mandatory": true,
2733
2733
  "category": 0,
2734
2734
  "erc20FormattedAmount": {
2735
+ "convertItsDecimal": false,
2735
2736
  "contractAddress": "{{parameters.tokenIn}}",
2736
2737
  "chain": "{{parameters.chainId}}"
2737
2738
  }
@@ -2986,104 +2987,6 @@ export const ACTIONS = {
2986
2987
  "image": "https://otomato-sdk-images.s3.eu-west-1.amazonaws.com/telegram.jpeg"
2987
2988
  }
2988
2989
  },
2989
- "TOKENS": {
2990
- "TRANSFER": {
2991
- "description": "Transfer token",
2992
- "chains": [
2993
- 34443
2994
- ],
2995
- "image": "https://otomato-sdk-images.s3.eu-west-1.amazonaws.com/Transfer.svg",
2996
- "TRANSFER": {
2997
- "name": "Transfer token",
2998
- "description": "Transfers an ERC20 token",
2999
- "type": 1,
3000
- "method": "function transfer(address to, uint256 value)",
3001
- "output": {
3002
- "transactionHash": "string"
3003
- },
3004
- "parameters": [
3005
- {
3006
- "key": "chainId",
3007
- "type": "chainId",
3008
- "description": "Chain ID of the network",
3009
- "mandatory": true,
3010
- "category": 0
3011
- },
3012
- {
3013
- "key": "contractAddress",
3014
- "type": "erc20",
3015
- "description": "The contract address of the ERC20",
3016
- "mandatory": true,
3017
- "category": 0
3018
- },
3019
- {
3020
- "key": "abiParams.to",
3021
- "type": "address",
3022
- "description": "Address to transfer crypto to",
3023
- "mandatory": true,
3024
- "category": 0
3025
- },
3026
- {
3027
- "key": "abiParams.value",
3028
- "type": "uint256",
3029
- "description": "Amount of crypto to transfer",
3030
- "mandatory": true,
3031
- "category": 0,
3032
- "erc20FormattedAmount": {
3033
- "contractAddress": "{{parameters.contractAddress}}",
3034
- "chain": "{{parameters.chainId}}"
3035
- }
3036
- },
3037
- ],
3038
- "checks": [
3039
- {
3040
- "type": 0,
3041
- "chainId": "{{parameters.chainId}}",
3042
- "contractAddress": "{{parameters.contractAddress}}",
3043
- "amount": "{{parameters.abi.parameters.value}}"
3044
- }
3045
- ],
3046
- "examples": [
3047
- {
3048
- "name": "Transfer USDC",
3049
- "description": "Transfer 100 USDC to vitalik.eth on Mode",
3050
- "parameters": [
3051
- {
3052
- "key": "chainId",
3053
- "value": 34443
3054
- },
3055
- {
3056
- "key": "abiParams.value",
3057
- "value": "100000000n"
3058
- },
3059
- {
3060
- "key": "abiParams.to",
3061
- "value": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
3062
- },
3063
- {
3064
- "key": "contractAddress",
3065
- "value": "0xd988097fb8612cc24eeC14542bC03424c656005f"
3066
- }
3067
- ]
3068
- }
3069
- ],
3070
- "permissions": {
3071
- "chainId": "{{parameters.chainId}}",
3072
- "approvedTargets": [
3073
- "{{parameters.contractAddress}}"
3074
- ],
3075
- "label": [
3076
- "Transfer {{tokenSymbol({{parameters.chainId}}, {{parameters.contractAddress}})}}"
3077
- ],
3078
- "labelNotAuthorized": [
3079
- "Transfer {{otherTokenSymbol({{parameters.chainId}}, {{parameters.contractAddress}})}}"
3080
- ]
3081
- },
3082
- "blockId": 100004,
3083
- "image": "https://otomato-sdk-images.s3.eu-west-1.amazonaws.com/Transfer.svg"
3084
- }
3085
- }
3086
- },
3087
2990
  "LENDING": {
3088
2991
  "IONIC": {
3089
2992
  "description": "#1 money market for Yield Bearing Assets on the OP Superchain",
@@ -3115,11 +3018,12 @@ export const ACTIONS = {
3115
3018
  },
3116
3019
  {
3117
3020
  "key": "abiParams.amount",
3118
- "type": "uint256",
3021
+ "type": "float",
3119
3022
  "description": "Amount of crypto to deposit",
3120
3023
  "mandatory": true,
3121
3024
  "category": 0,
3122
3025
  "erc20FormattedAmount": {
3026
+ "convertItsDecimal": false,
3123
3027
  "contractAddress": "{{parameters.tokenToDeposit}}",
3124
3028
  "chain": "{{parameters.chainId}}"
3125
3029
  }
@@ -3219,11 +3123,12 @@ export const ACTIONS = {
3219
3123
  },
3220
3124
  {
3221
3125
  "key": "abiParams.amount",
3222
- "type": "uint256",
3126
+ "type": "float",
3223
3127
  "description": "Amount of crypto to withdraw",
3224
3128
  "mandatory": true,
3225
3129
  "category": 0,
3226
3130
  "erc20FormattedAmount": {
3131
+ "convertItsDecimal": false,
3227
3132
  "contractAddress": "{{before.contractAddress}}",
3228
3133
  "chain": "{{parameters.chainId}}"
3229
3134
  }
@@ -3385,11 +3290,12 @@ export const ACTIONS = {
3385
3290
  },
3386
3291
  {
3387
3292
  "key": "abiParams.amount",
3388
- "type": "uint256",
3293
+ "type": "float",
3389
3294
  "description": "Amount of crypto to repay",
3390
3295
  "mandatory": true,
3391
3296
  "category": 0,
3392
3297
  "erc20FormattedAmount": {
3298
+ "convertItsDecimal": false,
3393
3299
  "contractAddress": "{{parameters.tokenToRepay}}",
3394
3300
  "chain": "{{parameters.chainId}}"
3395
3301
  }
@@ -3518,11 +3424,12 @@ export const ACTIONS = {
3518
3424
  },
3519
3425
  {
3520
3426
  "key": "abiParams.amount",
3521
- "type": "uint256",
3427
+ "type": "float",
3522
3428
  "description": "The amount of the asset to supply",
3523
3429
  "mandatory": true,
3524
3430
  "category": 0,
3525
3431
  "erc20FormattedAmount": {
3432
+ "convertItsDecimal": false,
3526
3433
  "contractAddress": "{{parameters.abi.parameters.asset}}",
3527
3434
  "chain": "{{parameters.chainId}}"
3528
3435
  }
@@ -3628,11 +3535,12 @@ export const ACTIONS = {
3628
3535
  },
3629
3536
  {
3630
3537
  "key": "abiParams.amount",
3631
- "type": "uint256",
3538
+ "type": "float",
3632
3539
  "description": "The amount of the asset to withdraw. Use type(uint).max for full balance.",
3633
3540
  "mandatory": true,
3634
3541
  "category": 0,
3635
3542
  "erc20FormattedAmount": {
3543
+ "convertItsDecimal": false,
3636
3544
  "contractAddress": "{{parameters.abi.parameters.asset}}",
3637
3545
  "chain": "{{parameters.chainId}}"
3638
3546
  },
@@ -3745,11 +3653,12 @@ export const ACTIONS = {
3745
3653
  },
3746
3654
  {
3747
3655
  "key": "abiParams.amount",
3748
- "type": "uint256",
3656
+ "type": "float",
3749
3657
  "description": "Amount of token to deposit",
3750
3658
  "mandatory": true,
3751
3659
  "category": 0,
3752
3660
  "erc20FormattedAmount": {
3661
+ "convertItsDecimal": false,
3753
3662
  "contractAddress": "{{parameters.tokenToDeposit}}",
3754
3663
  "chain": "{{parameters.chainId}}"
3755
3664
  }
@@ -3849,11 +3758,12 @@ export const ACTIONS = {
3849
3758
  },
3850
3759
  {
3851
3760
  "key": "abiParams.amount",
3852
- "type": "uint256",
3761
+ "type": "float",
3853
3762
  "description": "Amount of token to withdraw",
3854
3763
  "mandatory": true,
3855
3764
  "category": 0,
3856
3765
  "erc20FormattedAmount": {
3766
+ "convertItsDecimal": false,
3857
3767
  "contractAddress": "{{parameters.tokenToWithdraw}}",
3858
3768
  "chain": "{{parameters.chainId}}"
3859
3769
  }
@@ -3943,11 +3853,12 @@ export const ACTIONS = {
3943
3853
  },
3944
3854
  {
3945
3855
  "key": "abiParams.amount",
3946
- "type": "uint256",
3856
+ "type": "float",
3947
3857
  "description": "Amount of crypto to borrow",
3948
3858
  "mandatory": true,
3949
3859
  "category": 0,
3950
3860
  "erc20FormattedAmount": {
3861
+ "convertItsDecimal": false,
3951
3862
  "contractAddress": "{{parameters.tokenToBorrow}}",
3952
3863
  "chain": "{{parameters.chainId}}"
3953
3864
  }
@@ -4015,11 +3926,12 @@ export const ACTIONS = {
4015
3926
  },
4016
3927
  {
4017
3928
  "key": "abiParams.amount",
4018
- "type": "uint256",
3929
+ "type": "float",
4019
3930
  "description": "Amount of crypto to repay",
4020
3931
  "mandatory": true,
4021
3932
  "category": 0,
4022
3933
  "erc20FormattedAmount": {
3934
+ "convertItsDecimal": false,
4023
3935
  "contractAddress": "{{parameters.tokenToRepay}}",
4024
3936
  "chain": "{{parameters.chainId}}"
4025
3937
  }
@@ -4147,11 +4059,12 @@ export const ACTIONS = {
4147
4059
  },
4148
4060
  {
4149
4061
  "key": "abiParams.amount",
4150
- "type": "uint256",
4062
+ "type": "float",
4151
4063
  "description": "Amount of crypto to deposit",
4152
4064
  "mandatory": true,
4153
4065
  "category": 0,
4154
4066
  "erc20FormattedAmount": {
4067
+ "convertItsDecimal": false,
4155
4068
  "contractAddress": "{{parameters.abi.parameters.asset}}",
4156
4069
  "chain": "{{parameters.chainId}}"
4157
4070
  }
@@ -4234,11 +4147,12 @@ export const ACTIONS = {
4234
4147
  },
4235
4148
  {
4236
4149
  "key": "abiParams.amount",
4237
- "type": "uint256",
4150
+ "type": "float",
4238
4151
  "description": "Amount of crypto to withdraw",
4239
4152
  "mandatory": true,
4240
4153
  "category": 0,
4241
4154
  "erc20FormattedAmount": {
4155
+ "convertItsDecimal": false,
4242
4156
  "contractAddress": "{{parameters.abi.parameters.asset}}",
4243
4157
  "chain": "{{parameters.chainId}}"
4244
4158
  }
@@ -4337,11 +4251,12 @@ export const ACTIONS = {
4337
4251
  },
4338
4252
  {
4339
4253
  "key": "abiParams.amount",
4340
- "type": "uint256",
4254
+ "type": "float",
4341
4255
  "description": "The amount of the asset to supply",
4342
4256
  "mandatory": true,
4343
4257
  "category": 0,
4344
4258
  "erc20FormattedAmount": {
4259
+ "convertItsDecimal": false,
4345
4260
  "contractAddress": "{{parameters.abi.parameters.asset}}",
4346
4261
  "chain": "{{parameters.chainId}}"
4347
4262
  }
@@ -4447,11 +4362,12 @@ export const ACTIONS = {
4447
4362
  },
4448
4363
  {
4449
4364
  "key": "abiParams.amount",
4450
- "type": "uint256",
4365
+ "type": "float",
4451
4366
  "description": "The amount of the asset to withdraw. Use type(uint).max for full balance.",
4452
4367
  "mandatory": true,
4453
4368
  "category": 0,
4454
4369
  "erc20FormattedAmount": {
4370
+ "convertItsDecimal": false,
4455
4371
  "contractAddress": "{{parameters.abi.parameters.asset}}",
4456
4372
  "chain": "{{parameters.chainId}}"
4457
4373
  },
@@ -4612,11 +4528,12 @@ export const ACTIONS = {
4612
4528
  },
4613
4529
  {
4614
4530
  "key": "amount",
4615
- "type": "uint256",
4531
+ "type": "float",
4616
4532
  "description": "Amount to sell",
4617
4533
  "mandatory": true,
4618
4534
  "category": 0,
4619
4535
  "erc20FormattedAmount": {
4536
+ "convertItsDecimal": false,
4620
4537
  "contractAddress": "{{parameters.tokenIn}}",
4621
4538
  "chain": "{{parameters.chainId}}"
4622
4539
  }
@@ -1,4 +1,4 @@
1
- export const SDK_VERSION = '2.0.71';
1
+ export const SDK_VERSION = '2.0.72';
2
2
  export function compareVersions(v1, v2) {
3
3
  // Split the version strings into parts
4
4
  const v1Parts = v1.split('.').map(Number);
@@ -3,116 +3,192 @@ export const xSpacing = 500;
3
3
  export const ySpacing = 120;
4
4
  export const ROOT_X = 400;
5
5
  export const ROOT_Y = 120;
6
- export function positionWorkflowNodes(workflow) {
7
- try {
8
- // Step 1: Find the starting nodes using identityStartingNodes function
9
- const startingNodes = identityStartingNodes(workflow);
10
- // Step 2: Place the starting nodes
11
- let xPosition = ROOT_X;
12
- startingNodes.forEach((startNode) => {
13
- startNode.setPosition(xPosition, ROOT_Y);
14
- xPosition += xSpacing;
15
- });
16
- // Step 3: Place all other nodes relative to their parents
17
- const nodesToPosition = workflow.nodes.filter((node) => !startingNodes.includes(node));
18
- nodesToPosition.forEach((node) => positionNode(node, workflow.edges, xSpacing, ySpacing, workflow));
6
+ /**
7
+ * Helper: Returns a group key for a node based on its primary parent.
8
+ * If a node has multiple parents, we sort them numerically (by their ref)
9
+ * and choose the lowest one.
10
+ */
11
+ function getGroupKey(node, edges) {
12
+ const parents = getParents(node, edges);
13
+ if (parents.length === 0)
14
+ return "none";
15
+ parents.sort((a, b) => Number(a.getRef()) - Number(b.getRef()));
16
+ return parents[0].getRef();
17
+ }
18
+ /**
19
+ * Step 1: Layer Assignment via Topological Ordering.
20
+ */
21
+ function assignLayers(workflow) {
22
+ const incomingCounts = new Map();
23
+ for (const node of workflow.nodes) {
24
+ incomingCounts.set(node.getRef(), 0);
25
+ node.layer = 0;
19
26
  }
20
- catch (e) {
21
- console.error(e);
27
+ for (const edge of workflow.edges) {
28
+ const targetRef = edge.target.getRef();
29
+ incomingCounts.set(targetRef, (incomingCounts.get(targetRef) || 0) + 1);
22
30
  }
23
- }
24
- export function positionNode(node, edges, xSpacing, ySpacing, workflow) {
25
- const parents = getParents(node, edges);
26
- // todo: what if we have multiple parents?
27
- const children = getChildren(parents[0], edges);
28
- const sortedChildren = children.sort((a, b) => {
29
- var _a, _b;
30
- const edgeA = edges.find(edge => edge.source === parents[0] && edge.target === a);
31
- const edgeB = edges.find(edge => edge.source === parents[0] && edge.target === b);
32
- const labelA = (_a = edgeA === null || edgeA === void 0 ? void 0 : edgeA.label) !== null && _a !== void 0 ? _a : "";
33
- const labelB = (_b = edgeB === null || edgeB === void 0 ? void 0 : edgeB.label) !== null && _b !== void 0 ? _b : "";
34
- if (labelA === "true" && labelB !== "true")
35
- return -1;
36
- if (labelB === "true" && labelA !== "true")
37
- return 1;
38
- if (labelA === "false" && labelB !== "false")
39
- return 1;
40
- if (labelB === "false" && labelA !== "false")
41
- return -1;
42
- return 0;
43
- });
44
- const childrenCountOfParent = sortedChildren.length;
45
- const parentX = parents.reduce((sum, parent) => { var _a, _b; return sum + ((_b = (_a = parent.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : ROOT_X); }, 0) / parents.length;
46
- const parentY = Math.max(...parents.map(parent => { var _a, _b; return (_b = (_a = parent.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : ROOT_Y; }));
47
- if (childrenCountOfParent === 1) {
48
- node.setPosition(parentX, parentY + ySpacing);
31
+ const queue = [];
32
+ for (const node of workflow.nodes) {
33
+ if (incomingCounts.get(node.getRef()) === 0) {
34
+ queue.push(node);
35
+ node.layer = 0;
36
+ }
49
37
  }
50
- else {
51
- const index = sortedChildren.indexOf(node); // Get the position of this node among its siblings
52
- const totalChildren = sortedChildren.length;
53
- // Compute the x position for this node
54
- const offset = index - (totalChildren - 1) / 2; // Center the children around the parent
55
- node.setPosition(parentX + offset * xSpacing, parentY + ySpacing);
38
+ while (queue.length > 0) {
39
+ const current = queue.shift();
40
+ const currentLayer = current.layer;
41
+ const children = getChildren(current, workflow.edges);
42
+ for (const child of children) {
43
+ child.layer = Math.max(child.layer, currentLayer + 1);
44
+ const childRef = child.getRef();
45
+ incomingCounts.set(childRef, (incomingCounts.get(childRef) || 1) - 1);
46
+ if (incomingCounts.get(childRef) === 0) {
47
+ queue.push(child);
48
+ }
49
+ }
56
50
  }
57
51
  }
58
- export function positionWorkflowNodesAvoidOverlap(workflow) {
59
- const levels = new Map();
60
- function addToLevel(node) {
61
- const level = Math.round(node.position.y / ySpacing);
62
- if (!levels.has(level)) {
63
- levels.set(level, []);
52
+ /**
53
+ * Main function that positions workflow nodes using a hierarchical layout.
54
+ * Nodes are grouped by their primary parent.
55
+ */
56
+ export function positionWorkflowNodes(workflow) {
57
+ try {
58
+ // Step 1: Assign layers.
59
+ assignLayers(workflow);
60
+ // Group nodes by layer.
61
+ const layers = new Map();
62
+ for (const node of workflow.nodes) {
63
+ const layer = node.layer;
64
+ if (!layers.has(layer)) {
65
+ layers.set(layer, []);
66
+ }
67
+ layers.get(layer).push(node);
64
68
  }
65
- levels.get(level).push(node);
66
- }
67
- // 1) Lay out nodes using existing logic
68
- positionWorkflowNodes(workflow);
69
- // 2) Fill the `levels` map
70
- workflow.nodes.forEach((node) => {
71
- if (node.position) {
72
- addToLevel(node);
69
+ // Process each layer in order.
70
+ const sortedLayers = Array.from(layers.keys()).sort((a, b) => a - b);
71
+ for (const layer of sortedLayers) {
72
+ const nodesInLayer = layers.get(layer);
73
+ // Group nodes by primary parent.
74
+ const groups = new Map();
75
+ for (const node of nodesInLayer) {
76
+ const groupKey = getGroupKey(node, workflow.edges);
77
+ if (!groups.has(groupKey)) {
78
+ groups.set(groupKey, []);
79
+ }
80
+ groups.get(groupKey).push(node);
81
+ }
82
+ // Determine the Y position for this layer.
83
+ const yPos = (layer * ySpacing) + ROOT_Y;
84
+ const groupInfos = [];
85
+ // Sort group keys numerically.
86
+ const sortedGroupKeys = Array.from(groups.keys()).sort((a, b) => Number(a) - Number(b));
87
+ for (const key of sortedGroupKeys) {
88
+ const groupNodes = groups.get(key);
89
+ groupNodes.sort((a, b) => Number(a.getRef()) - Number(b.getRef()));
90
+ let desiredCenter;
91
+ if (key === "none") {
92
+ desiredCenter = ROOT_X;
93
+ }
94
+ else {
95
+ const parent = workflow.nodes.find(n => n.getRef() === key);
96
+ desiredCenter = parent && parent.position ? parent.position.x : ROOT_X;
97
+ }
98
+ const groupSize = groupNodes.length;
99
+ const width = (groupSize - 1) * xSpacing;
100
+ const desiredLeft = desiredCenter - width / 2;
101
+ groupInfos.push({
102
+ groupKey: key,
103
+ nodes: groupNodes,
104
+ desiredCenter,
105
+ desiredLeft,
106
+ width
107
+ });
108
+ }
109
+ // Adjust groups so that adjacent groups do not overlap.
110
+ // We require that the left edge of a group is at least xSpacing to the right of the previous group's right edge.
111
+ groupInfos.sort((a, b) => a.desiredLeft - b.desiredLeft);
112
+ let prevRight = -Infinity;
113
+ for (const group of groupInfos) {
114
+ let newLeft = group.desiredLeft;
115
+ if (newLeft < prevRight + xSpacing) {
116
+ newLeft = prevRight + xSpacing;
117
+ }
118
+ group.newLeft = newLeft;
119
+ prevRight = newLeft + group.width;
120
+ }
121
+ // Now assign positions for nodes in each group using the new left.
122
+ for (const group of groupInfos) {
123
+ const { nodes, newLeft } = group;
124
+ for (let i = 0; i < nodes.length; i++) {
125
+ const nodeX = newLeft + i * xSpacing;
126
+ nodes[i].setPosition(nodeX, yPos);
127
+ }
128
+ }
73
129
  }
74
- });
75
- // 3) Resolve horizontal overlaps and enforce parent grouping
76
- levels.forEach((nodes, level) => {
77
- // Group nodes by their parent
78
- const parentGroups = new Map();
79
- nodes.forEach((node) => {
80
- const parents = getParents(node, workflow.edges);
81
- if (parents.length > 0) {
82
- const parent = parents[0]; // Assuming single parent for simplicity
83
- if (!parentGroups.has(parent)) {
84
- parentGroups.set(parent, []);
130
+ // Step 3: Resolve overlaps within each group in each layer.
131
+ layers.forEach((nodes, layer) => {
132
+ const groups = new Map();
133
+ for (const node of nodes) {
134
+ const groupKey = getGroupKey(node, workflow.edges);
135
+ if (!groups.has(groupKey)) {
136
+ groups.set(groupKey, []);
85
137
  }
86
- parentGroups.get(parent).push(node);
138
+ groups.get(groupKey).push(node);
87
139
  }
140
+ groups.forEach((groupNodes, groupKey) => {
141
+ let changed = true;
142
+ while (changed) {
143
+ changed = false;
144
+ groupNodes.sort((a, b) => {
145
+ const diff = a.position.x - b.position.x;
146
+ if (Math.abs(diff) < 1e-6) {
147
+ return Number(a.getRef()) - Number(b.getRef());
148
+ }
149
+ return diff;
150
+ });
151
+ for (let i = 1; i < groupNodes.length; i++) {
152
+ const prev = groupNodes[i - 1];
153
+ const current = groupNodes[i];
154
+ const diff = current.position.x - prev.position.x;
155
+ if (diff < xSpacing) {
156
+ const shift = xSpacing - diff;
157
+ moveNodeAndChildren(current, shift, workflow.edges);
158
+ changed = true;
159
+ }
160
+ }
161
+ }
162
+ });
88
163
  });
89
- // Flatten the groups back into a sorted array
90
- const groupedNodes = [];
91
- parentGroups.forEach((group) => {
92
- groupedNodes.push(...group);
164
+ // Step 4: Bottom-up Parent Centering.
165
+ // Now, we simply set each parent's x to the exact average of its children.
166
+ centerParentXPositions(workflow);
167
+ // Final sort for consistency: sort nodes in each layer by primary parent's numeric value then node ref.
168
+ layers.forEach((nodes, layer) => {
169
+ nodes.sort((a, b) => {
170
+ const groupA = Number(getGroupKey(a, workflow.edges));
171
+ const groupB = Number(getGroupKey(b, workflow.edges));
172
+ if (groupA !== groupB)
173
+ return groupA - groupB;
174
+ return Number(a.getRef()) - Number(b.getRef());
175
+ });
93
176
  });
94
- // Sort nodes by x within each group to detect overlaps
95
- groupedNodes.sort((a, b) => { var _a, _b; return ((_a = a.position.x) !== null && _a !== void 0 ? _a : 0) - ((_b = b.position.x) !== null && _b !== void 0 ? _b : 0); });
96
- // Shift nodes within groups to resolve overlaps
97
- for (let i = 1; i < groupedNodes.length; i++) {
98
- const prev = groupedNodes[i - 1];
99
- const current = groupedNodes[i];
100
- if (current.position.x - prev.position.x < xSpacing) {
101
- const shift = xSpacing - (current.position.x - prev.position.x);
102
- moveNodeAndChildren(current, shift, workflow.edges);
103
- }
104
- }
105
- });
106
- // 4) **Center each parent over its children** (the existing step)
177
+ }
178
+ catch (e) {
179
+ console.error(e);
180
+ }
181
+ }
182
+ export function positionWorkflowNodesAvoidOverlap(workflow) {
183
+ positionWorkflowNodes(workflow);
107
184
  centerParentXPositions(workflow);
108
185
  }
109
186
  /**
110
- * Repositions each parent node so that its X is the average of the children’s X.
111
- * We do a simple bottom-up pass: start with all leaves, then move upward to parents.
187
+ * Modified bottom-up pass to center each parent over its children.
188
+ * The parent's x is now set exactly to the average of its children's x positions.
112
189
  */
113
190
  function centerParentXPositions(workflow) {
114
191
  var _a, _b;
115
- // Identify the “leaf” nodes
116
192
  const queue = identifyLeafNodes(workflow);
117
193
  while (queue.length > 0) {
118
194
  const child = queue.shift();
@@ -120,43 +196,32 @@ function centerParentXPositions(workflow) {
120
196
  for (const parent of parents) {
121
197
  const children = getChildren(parent, workflow.edges);
122
198
  if (children.length) {
123
- // Average x of all children
124
- const sumX = children.reduce((acc, c) => { var _a, _b; return acc + ((_b = (_a = c.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0); }, 0);
125
- const avgX = sumX / children.length;
126
- // Move parent to that average, keep same y
199
+ const xs = children.map(c => c.position.x);
200
+ const avgX = xs.reduce((acc, x) => acc + x, 0) / xs.length;
127
201
  parent.setPosition(avgX, (_b = (_a = parent.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : 0);
128
202
  }
129
- // Add this parent to the queue so we can recurse upward
130
203
  if (!queue.includes(parent)) {
131
204
  queue.push(parent);
132
205
  }
133
206
  }
134
207
  }
135
208
  }
209
+ /**
210
+ * Recursively shifts a node and all its descendants by the given amount.
211
+ */
136
212
  function moveNodeAndChildren(node, shift, edges) {
137
- // Move the node
138
- node.setPosition(node.position.x + shift, node.position.y);
139
- // Propagate to children
140
- edges
141
- .filter((edge) => edge.source === node)
142
- .forEach((edge) => {
143
- moveNodeAndChildren(edge.target, shift, edges);
144
- });
213
+ const oldX = node.position.x;
214
+ node.setPosition(oldX + shift, node.position.y);
215
+ edges.filter(edge => edge.source === node)
216
+ .forEach(edge => moveNodeAndChildren(edge.target, shift, edges));
145
217
  }
146
218
  export function identifyLeafNodes(workflow) {
147
219
  const nonLeafNodes = new Set(workflow.edges.map(edge => edge.source.getRef()));
148
220
  return workflow.nodes.filter(node => !nonLeafNodes.has(node.getRef()));
149
221
  }
150
- /**
151
- * Identifies starting nodes (nodes with no incoming edges).
152
- * A starting node is defined as one that is not a target of any edge.
153
- *
154
- * @param workflow The workflow to analyze.
155
- * @returns An array of nodes that have no incoming edges.
156
- */
157
222
  export function identityStartingNodes(workflow) {
158
- const childRefs = new Set(workflow.edges.map((edge) => edge.target.getRef()));
159
- return workflow.nodes.filter((node) => !childRefs.has(node.getRef()));
223
+ const childRefs = new Set(workflow.edges.map(edge => edge.target.getRef()));
224
+ return workflow.nodes.filter(node => !childRefs.has(node.getRef()));
160
225
  }
161
226
  export function getChildren(node, edges) {
162
227
  return edges.filter(edge => edge.source === node).map(edge => edge.target);
@@ -169,7 +234,7 @@ export function getEdges(node, edges) {
169
234
  }
170
235
  export function getEndNodePositions(workflow) {
171
236
  return workflow.nodes
172
- .filter(node => getChildren(node, workflow.edges).length === 0) // node with no children
237
+ .filter(node => getChildren(node, workflow.edges).length === 0)
173
238
  .map(node => {
174
239
  var _a, _b, _c, _d;
175
240
  return ({
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Example workflow:
3
+ * Trigger -> Delay -> Condition
4
+ * ├─(true)─> Split(3)
5
+ * └─(false)-> Delay
6
+ */
7
+ export declare function conditionSplitDemo(): Promise<void>;
@@ -1336,48 +1336,6 @@ export declare const ACTIONS: {
1336
1336
  image: string;
1337
1337
  };
1338
1338
  };
1339
- TOKENS: {
1340
- TRANSFER: {
1341
- description: string;
1342
- chains: number[];
1343
- image: string;
1344
- TRANSFER: {
1345
- name: string;
1346
- description: string;
1347
- type: number;
1348
- method: string;
1349
- output: {
1350
- transactionHash: string;
1351
- };
1352
- parameters: Parameter[];
1353
- checks: {
1354
- type: number;
1355
- chainId: string;
1356
- contractAddress: string;
1357
- amount: string;
1358
- }[];
1359
- examples: {
1360
- name: string;
1361
- description: string;
1362
- parameters: ({
1363
- key: string;
1364
- value: number;
1365
- } | {
1366
- key: string;
1367
- value: string;
1368
- })[];
1369
- }[];
1370
- permissions: {
1371
- chainId: string;
1372
- approvedTargets: string[];
1373
- label: string[];
1374
- labelNotAuthorized: string[];
1375
- };
1376
- blockId: number;
1377
- image: string;
1378
- };
1379
- };
1380
- };
1381
1339
  LENDING: {
1382
1340
  IONIC: {
1383
1341
  description: string;
@@ -1,2 +1,2 @@
1
- export declare const SDK_VERSION = "2.0.71";
1
+ export declare const SDK_VERSION = "2.0.72";
2
2
  export declare function compareVersions(v1: string, v2: string): number;
@@ -5,17 +5,13 @@ export declare const xSpacing = 500;
5
5
  export declare const ySpacing = 120;
6
6
  export declare const ROOT_X = 400;
7
7
  export declare const ROOT_Y = 120;
8
+ /**
9
+ * Main function that positions workflow nodes using a hierarchical layout.
10
+ * Nodes are grouped by their primary parent.
11
+ */
8
12
  export declare function positionWorkflowNodes(workflow: Workflow): void;
9
- export declare function positionNode(node: Node, edges: Edge[], xSpacing: number, ySpacing: number, workflow: Workflow): void;
10
13
  export declare function positionWorkflowNodesAvoidOverlap(workflow: Workflow): void;
11
14
  export declare function identifyLeafNodes(workflow: Workflow): Node[];
12
- /**
13
- * Identifies starting nodes (nodes with no incoming edges).
14
- * A starting node is defined as one that is not a target of any edge.
15
- *
16
- * @param workflow The workflow to analyze.
17
- * @returns An array of nodes that have no incoming edges.
18
- */
19
15
  export declare function identityStartingNodes(workflow: Workflow): Node[];
20
16
  export declare function getChildren(node: Node, edges: Edge[]): Node[];
21
17
  export declare function getParents(node: Node, edges: Edge[]): Node[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "otomato-sdk",
3
- "version": "2.0.71",
3
+ "version": "2.0.72",
4
4
  "description": "An SDK for building and managing automations on Otomato",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/types/src/index.d.ts",