Skip to content

Commit 99542ec

Browse files
committed
smart contract tx with remote signer
1 parent 4848b75 commit 99542ec

File tree

2 files changed

+143
-15
lines changed

2 files changed

+143
-15
lines changed

operator/index.ts

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,27 @@ import { delegationABI } from "./abis/delegationABI";
44
import { contractABI } from './abis/contractABI';
55
import { registryABI } from './abis/registryABI';
66
import { avsDirectoryABI } from './abis/avsDirectoryABI';
7+
import { RemoteSigner } from "./remoteSigner";
78
dotenv.config();
89

10+
const remoteSignerUrl = process.env.REMOTE_SIGNER_URL!;
11+
const operatorAddress = process.env.OPERATOR_ADDRESS!;
912
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
1013
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
14+
const signer = new RemoteSigner(operatorAddress, provider, remoteSignerUrl);
15+
let address = "";
1116

1217
const delegationManagerAddress = process.env.DELEGATION_MANAGER_ADDRESS!;
1318
const contractAddress = process.env.CONTRACT_ADDRESS!;
1419
const stakeRegistryAddress = process.env.STAKE_REGISTRY_ADDRESS!;
1520
const avsDirectoryAddress = process.env.AVS_DIRECTORY_ADDRESS!;
1621

17-
const remoteSignerUrl = process.env.REMOTE_SIGNER_URL!;
18-
const operatorAddress = process.env.OPERATOR_ADDRESS!;
19-
2022
const signerType = process.env.SIGNER_TYPE!;
2123

22-
const delegationManager = new ethers.Contract(delegationManagerAddress, delegationABI, wallet);
23-
const contract = new ethers.Contract(contractAddress, contractABI, wallet);
24-
const registryContract = new ethers.Contract(stakeRegistryAddress, registryABI, wallet);
25-
const avsDirectory = new ethers.Contract(avsDirectoryAddress, avsDirectoryABI, wallet);
24+
let delegationManager: ethers.Contract;
25+
let contract: ethers.Contract;
26+
let registryContract: ethers.Contract;
27+
let avsDirectory: ethers.Contract;
2628

2729
const signAndRespondToTask = async (taskIndex: number, taskCreatedBlock: number, taskName: string) => {
2830
const message = `Hello, ${taskName}`;
@@ -35,11 +37,7 @@ const signAndRespondToTask = async (taskIndex: number, taskCreatedBlock: number,
3537
signature = await wallet.signMessage(messageBytes);
3638
} else if (signerType === "remote") {
3739
console.log("Using remote signer to sign message")
38-
signature = await callJsonRpcEndpoint(
39-
remoteSignerUrl,
40-
"eth_sign",
41-
[operatorAddress, messageHash]
42-
);
40+
signature = await signer.signMessage(messageHash);
4341
}
4442

4543
console.log(
@@ -58,7 +56,7 @@ const signAndRespondToTask = async (taskIndex: number, taskCreatedBlock: number,
5856
const registerOperator = async () => {
5957
console.log("check")
6058
const tx1 = await delegationManager.registerAsOperator({
61-
earningsReceiver: await wallet.address,
59+
earningsReceiver: address,
6260
delegationApprover: "0x0000000000000000000000000000000000000000",
6361
stakerOptOutWindowBlocks: 0
6462
}, "");
@@ -77,13 +75,15 @@ const registerOperator = async () => {
7775

7876
// Calculate the digest hash using the avsDirectory's method
7977
const digestHash = await avsDirectory.calculateOperatorAVSRegistrationDigestHash(
80-
wallet.address,
78+
address,
8179
contract.address,
8280
salt,
8381
expiry
8482
);
8583

8684
// // Sign the digest hash with the operator's private key
85+
// TODO(shrimalmadhur): I am not completely sure about how to make this work with remote signer
86+
// as the signDigest function is not available on the remote signer.
8787
const signingKey = new ethers.utils.SigningKey(process.env.PRIVATE_KEY!);
8888
const signature = signingKey.signDigest(digestHash);
8989

@@ -92,7 +92,7 @@ const registerOperator = async () => {
9292

9393
const tx2 = await registryContract.registerOperatorWithSignature(
9494
operatorSignature,
95-
wallet.address
95+
address
9696
);
9797
await tx2.wait();
9898
console.log("Operator registered on AVS successfully");
@@ -110,6 +110,19 @@ const monitorNewTasks = async () => {
110110
};
111111

112112
const main = async () => {
113+
if (signerType === "local") {
114+
address = wallet.address;
115+
delegationManager = new ethers.Contract(delegationManagerAddress, delegationABI, wallet);
116+
contract = new ethers.Contract(contractAddress, contractABI, wallet);
117+
registryContract = new ethers.Contract(stakeRegistryAddress, registryABI, wallet);
118+
avsDirectory = new ethers.Contract(avsDirectoryAddress, avsDirectoryABI, wallet);
119+
} else {
120+
address = await signer.getAddress();
121+
delegationManager = new ethers.Contract(delegationManagerAddress, delegationABI, signer);
122+
contract = new ethers.Contract(contractAddress, contractABI, signer);
123+
registryContract = new ethers.Contract(stakeRegistryAddress, registryABI, signer);
124+
avsDirectory = new ethers.Contract(avsDirectoryAddress, avsDirectoryABI, signer);
125+
}
113126
await registerOperator();
114127
monitorNewTasks().catch((error) => {
115128
console.error("Error monitoring tasks:", error);

operator/remoteSigner.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { Signer, providers, utils } from 'ethers';
2+
3+
export class RemoteSigner extends Signer {
4+
private readonly address: string;
5+
readonly provider: providers.Provider;
6+
private readonly remoteSigningEndpoint: string;
7+
8+
constructor(address: string, provider: providers.Provider, remoteSigningEndpoint: string) {
9+
super();
10+
this.address = address;
11+
this.provider = provider;
12+
this.remoteSigningEndpoint = remoteSigningEndpoint;
13+
}
14+
15+
async getAddress(): Promise<string> {
16+
return this.address;
17+
}
18+
19+
async signMessage(message: string | utils.Bytes): Promise<string> {
20+
if (typeof(message) === "string") {
21+
return this.signMessageHash(message);
22+
} else {
23+
const messageHash = utils.solidityKeccak256(["string"], [message])
24+
return this.signMessageHash(messageHash);
25+
}
26+
}
27+
28+
async signTransaction(transaction: utils.Deferrable<providers.TransactionRequest>): Promise<string> {
29+
// Implement the logic to send the transaction to your remote signing service
30+
// and return the signed transaction
31+
const tx = {
32+
from: transaction.from,
33+
to: transaction.to,
34+
value: transaction.value,
35+
gas: transaction.gasLimit?.toString(),
36+
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas?.toString(),
37+
maxFeePerGas: transaction.maxFeePerGas?.toString(),
38+
nonce: transaction.nonce,
39+
data: transaction.data,
40+
}
41+
const signedTransaction = await callJsonRpcEndpoint(
42+
this.remoteSigningEndpoint,
43+
"eth_signTransaction",
44+
[tx]
45+
);
46+
47+
return signedTransaction;
48+
}
49+
50+
connect(provider: providers.Provider): Signer {
51+
return new RemoteSigner(this.address, provider, this.remoteSigningEndpoint);
52+
}
53+
54+
private async signMessageHash(messageHash: string): Promise<string> {
55+
// Implement the logic to send the message hash to your remote signing service
56+
// and return the signature
57+
const signature = await callJsonRpcEndpoint(
58+
this.remoteSigningEndpoint,
59+
"eth_sign",
60+
[this.address, messageHash]
61+
);
62+
63+
return signature;
64+
}
65+
}
66+
67+
interface JsonRpcRequest {
68+
jsonrpc: string;
69+
method: string;
70+
params: any[];
71+
id: number;
72+
}
73+
74+
interface JsonRpcResponse {
75+
jsonrpc: string;
76+
result?: any;
77+
error?: {
78+
code: number;
79+
message: string;
80+
};
81+
id: number;
82+
}
83+
84+
async function callJsonRpcEndpoint(
85+
url: string,
86+
method: string,
87+
params: any[] = []
88+
): Promise<any> {
89+
const request: JsonRpcRequest = {
90+
jsonrpc: "2.0",
91+
method,
92+
params,
93+
id: 1, // You might want to generate a unique id for each request
94+
};
95+
96+
const response = await fetch(url, {
97+
method: "POST",
98+
headers: {
99+
"Content-Type": "application/json",
100+
},
101+
body: JSON.stringify(request),
102+
});
103+
104+
if (!response.ok) {
105+
throw new Error(`HTTP error! status: ${response.status}`);
106+
}
107+
108+
const jsonResponse: JsonRpcResponse = await response.json();
109+
110+
if (jsonResponse.error) {
111+
throw new Error(`JSON-RPC error: ${jsonResponse.error.message}`);
112+
}
113+
114+
return jsonResponse.result;
115+
}

0 commit comments

Comments
 (0)