Skip to content

Commit fbc543c

Browse files
Merge pull request #14 from techops-services/KEEP-1155-write-smart-contract
Keep 1155 Write smart contract action
2 parents 5893ab0 + 6eff4ac commit fbc543c

File tree

6 files changed

+387
-30
lines changed

6 files changed

+387
-30
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ Visit [http://localhost:3000](http://localhost:3000) to get started.
9090
- **Resend**: Send Email
9191
- **Slack**: Send Slack Message
9292
- **v0**: Create Chat, Send Message
93-
- **Web3**: Transfer Funds, Read Contract
93+
- **Web3**: Transfer Funds, Read Contract, Write Contract
9494
<!-- PLUGINS:END -->
9595

9696
## Code Generation

components/workflow/config/action-config-renderer.tsx

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ function SchemaBuilderField(props: FieldProps) {
116116

117117
type AbiFunctionSelectProps = FieldProps & {
118118
abiValue: string;
119+
functionFilter?: "read" | "write";
119120
};
120121

121122
function AbiFunctionSelectField({
@@ -124,6 +125,7 @@ function AbiFunctionSelectField({
124125
onChange,
125126
disabled,
126127
abiValue,
128+
functionFilter = "read",
127129
}: AbiFunctionSelectProps) {
128130
// Parse ABI and extract functions
129131
const functions = React.useMemo(() => {
@@ -137,27 +139,36 @@ function AbiFunctionSelectField({
137139
return [];
138140
}
139141

140-
// Extract all functions from the ABI
141-
return abi
142-
.filter((item) => item.type === "function")
143-
.map((func) => {
144-
const inputs = func.inputs || [];
145-
const params = inputs
146-
.map(
147-
(input: { name: string; type: string }) =>
148-
`${input.type} ${input.name}`
149-
)
150-
.join(", ");
151-
return {
152-
name: func.name,
153-
label: `${func.name}(${params})`,
154-
stateMutability: func.stateMutability || "nonpayable",
155-
};
156-
});
142+
// Filter functions based on functionFilter prop
143+
const filterFn =
144+
functionFilter === "write"
145+
? (item: { type: string; stateMutability?: string }) =>
146+
item.type === "function" &&
147+
item.stateMutability !== "view" &&
148+
item.stateMutability !== "pure"
149+
: (item: { type: string; stateMutability?: string }) =>
150+
item.type === "function" &&
151+
(item.stateMutability === "view" ||
152+
item.stateMutability === "pure");
153+
154+
return abi.filter(filterFn).map((func) => {
155+
const inputs = func.inputs || [];
156+
const params = inputs
157+
.map(
158+
(input: { name: string; type: string }) =>
159+
`${input.type} ${input.name}`
160+
)
161+
.join(", ");
162+
return {
163+
name: func.name,
164+
label: `${func.name}(${params})`,
165+
stateMutability: func.stateMutability || "nonpayable",
166+
};
167+
});
157168
} catch {
158169
return [];
159170
}
160-
}, [abiValue]);
171+
}, [abiValue, functionFilter]);
161172

162173
if (functions.length === 0) {
163174
return (
@@ -177,7 +188,7 @@ function AbiFunctionSelectField({
177188
<SelectContent>
178189
{functions.map((func) => (
179190
<SelectItem key={func.name} value={func.name}>
180-
<div className="flex flex-col">
191+
<div className="flex flex-col items-start">
181192
<span>{func.label}</span>
182193
<span className="text-muted-foreground text-xs">
183194
{func.stateMutability}
@@ -338,6 +349,7 @@ function renderField(
338349
abiValue={abiValue}
339350
disabled={disabled}
340351
field={field}
352+
functionFilter={field.functionFilter}
341353
onChange={(val) => onUpdateConfig(field.key, val)}
342354
value={value}
343355
/>

plugins/registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ export type ActionConfigFieldBase = {
6262
// For abi-function-select: which field contains the ABI JSON
6363
abiField?: string;
6464

65+
// For abi-function-select: filter functions by type ("read" or "write")
66+
functionFilter?: "read" | "write";
67+
6568
// For abi-function-args: which field contains the ABI JSON and selected function
6669
abiFunctionField?: string;
6770
};

plugins/web3/icon.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
export function Web3Icon({ className }: { className?: string }) {
22
return (
3-
<svg
4-
aria-label="Web3"
5-
className={className}
6-
fill="currentColor"
7-
viewBox="0 0 24 24"
8-
xmlns="http://www.w3.org/2000/svg"
9-
>
3+
<svg className={className} xmlns="http://www.w3.org/2000/svg" width="148" height="148" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" >
104
<title>Web3</title>
11-
{/* Blockchain/connected blocks icon */}
12-
<path d="M12 2L2 7v10l10 5 10-5V7L12 2zm0 2.18l8 4v8.64l-8 4-8-4V8.18l8-4z" />
13-
<path d="M12 6L6 9v6l6 3 6-3V9l-6-3zm0 2.18l3.82 1.91v3.82L12 15.82l-3.82-1.91V10.09L12 8.18z" />
5+
<circle cx="12" cy="12" r="10"/>
6+
<path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/>
7+
<path d="M2 12h20"/>
148
</svg>
159
);
1610
}

plugins/web3/index.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,77 @@ const web3Plugin: IntegrationPlugin = {
118118
},
119119
],
120120
},
121+
{
122+
slug: "write-contract",
123+
label: "Write Contract",
124+
description: "Write data to a smart contract (state-changing functions)",
125+
category: "Web3",
126+
stepFunction: "writeContractStep",
127+
stepImportPath: "write-contract",
128+
outputFields: [
129+
{
130+
field: "success",
131+
description: "Whether the contract call succeeded",
132+
},
133+
{
134+
field: "transactionHash",
135+
description: "The transaction hash of the successful write",
136+
},
137+
{
138+
field: "result",
139+
description: "The contract function return value (if any)",
140+
},
141+
{
142+
field: "error",
143+
description: "Error message if the call failed",
144+
},
145+
],
146+
configFields: [
147+
{
148+
key: "contractAddress",
149+
label: "Contract Address",
150+
type: "template-input",
151+
placeholder: "0x... or {{NodeName.contractAddress}}",
152+
example: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
153+
required: true,
154+
},
155+
{
156+
key: "network",
157+
label: "Network",
158+
type: "select",
159+
placeholder: "Select network",
160+
required: true,
161+
options: [
162+
{ label: "Ethereum Mainnet", value: "mainnet" },
163+
{ label: "Sepolia Testnet", value: "sepolia" },
164+
],
165+
},
166+
{
167+
key: "abi",
168+
label: "Contract ABI",
169+
type: "template-textarea",
170+
placeholder: "Paste contract ABI JSON here",
171+
rows: 6,
172+
required: true,
173+
},
174+
{
175+
key: "abiFunction",
176+
label: "Function",
177+
type: "abi-function-select",
178+
abiField: "abi",
179+
functionFilter: "write",
180+
placeholder: "Select a function",
181+
required: true,
182+
},
183+
{
184+
key: "functionArgs",
185+
label: "Function Arguments",
186+
type: "abi-function-args",
187+
abiField: "abi",
188+
abiFunctionField: "abiFunction",
189+
},
190+
],
191+
},
121192
],
122193
};
123194

0 commit comments

Comments
 (0)