Skip to content

Commit

Permalink
smart contract tx with remote signer
Browse files Browse the repository at this point in the history
  • Loading branch information
shrimalmadhur committed Sep 3, 2024
1 parent 4848b75 commit 99542ec
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 15 deletions.
43 changes: 28 additions & 15 deletions operator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@ import { delegationABI } from "./abis/delegationABI";
import { contractABI } from './abis/contractABI';
import { registryABI } from './abis/registryABI';
import { avsDirectoryABI } from './abis/avsDirectoryABI';
import { RemoteSigner } from "./remoteSigner";
dotenv.config();

const remoteSignerUrl = process.env.REMOTE_SIGNER_URL!;
const operatorAddress = process.env.OPERATOR_ADDRESS!;
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const signer = new RemoteSigner(operatorAddress, provider, remoteSignerUrl);
let address = "";

const delegationManagerAddress = process.env.DELEGATION_MANAGER_ADDRESS!;
const contractAddress = process.env.CONTRACT_ADDRESS!;
const stakeRegistryAddress = process.env.STAKE_REGISTRY_ADDRESS!;
const avsDirectoryAddress = process.env.AVS_DIRECTORY_ADDRESS!;

const remoteSignerUrl = process.env.REMOTE_SIGNER_URL!;
const operatorAddress = process.env.OPERATOR_ADDRESS!;

const signerType = process.env.SIGNER_TYPE!;

const delegationManager = new ethers.Contract(delegationManagerAddress, delegationABI, wallet);
const contract = new ethers.Contract(contractAddress, contractABI, wallet);
const registryContract = new ethers.Contract(stakeRegistryAddress, registryABI, wallet);
const avsDirectory = new ethers.Contract(avsDirectoryAddress, avsDirectoryABI, wallet);
let delegationManager: ethers.Contract;
let contract: ethers.Contract;
let registryContract: ethers.Contract;
let avsDirectory: ethers.Contract;

const signAndRespondToTask = async (taskIndex: number, taskCreatedBlock: number, taskName: string) => {
const message = `Hello, ${taskName}`;
Expand All @@ -35,11 +37,7 @@ const signAndRespondToTask = async (taskIndex: number, taskCreatedBlock: number,
signature = await wallet.signMessage(messageBytes);
} else if (signerType === "remote") {
console.log("Using remote signer to sign message")
signature = await callJsonRpcEndpoint(
remoteSignerUrl,
"eth_sign",
[operatorAddress, messageHash]
);
signature = await signer.signMessage(messageHash);
}

console.log(
Expand All @@ -58,7 +56,7 @@ const signAndRespondToTask = async (taskIndex: number, taskCreatedBlock: number,
const registerOperator = async () => {
console.log("check")
const tx1 = await delegationManager.registerAsOperator({
earningsReceiver: await wallet.address,
earningsReceiver: address,
delegationApprover: "0x0000000000000000000000000000000000000000",
stakerOptOutWindowBlocks: 0
}, "");
Expand All @@ -77,13 +75,15 @@ const registerOperator = async () => {

// Calculate the digest hash using the avsDirectory's method
const digestHash = await avsDirectory.calculateOperatorAVSRegistrationDigestHash(
wallet.address,
address,
contract.address,
salt,
expiry
);

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

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

const tx2 = await registryContract.registerOperatorWithSignature(
operatorSignature,
wallet.address
address
);
await tx2.wait();
console.log("Operator registered on AVS successfully");
Expand All @@ -110,6 +110,19 @@ const monitorNewTasks = async () => {
};

const main = async () => {
if (signerType === "local") {
address = wallet.address;
delegationManager = new ethers.Contract(delegationManagerAddress, delegationABI, wallet);
contract = new ethers.Contract(contractAddress, contractABI, wallet);
registryContract = new ethers.Contract(stakeRegistryAddress, registryABI, wallet);
avsDirectory = new ethers.Contract(avsDirectoryAddress, avsDirectoryABI, wallet);
} else {
address = await signer.getAddress();
delegationManager = new ethers.Contract(delegationManagerAddress, delegationABI, signer);
contract = new ethers.Contract(contractAddress, contractABI, signer);
registryContract = new ethers.Contract(stakeRegistryAddress, registryABI, signer);
avsDirectory = new ethers.Contract(avsDirectoryAddress, avsDirectoryABI, signer);
}
await registerOperator();
monitorNewTasks().catch((error) => {
console.error("Error monitoring tasks:", error);
Expand Down
115 changes: 115 additions & 0 deletions operator/remoteSigner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Signer, providers, utils } from 'ethers';

export class RemoteSigner extends Signer {
private readonly address: string;
readonly provider: providers.Provider;
private readonly remoteSigningEndpoint: string;

constructor(address: string, provider: providers.Provider, remoteSigningEndpoint: string) {
super();
this.address = address;
this.provider = provider;
this.remoteSigningEndpoint = remoteSigningEndpoint;
}

async getAddress(): Promise<string> {
return this.address;
}

async signMessage(message: string | utils.Bytes): Promise<string> {
if (typeof(message) === "string") {
return this.signMessageHash(message);
} else {
const messageHash = utils.solidityKeccak256(["string"], [message])
return this.signMessageHash(messageHash);
}
}

async signTransaction(transaction: utils.Deferrable<providers.TransactionRequest>): Promise<string> {
// Implement the logic to send the transaction to your remote signing service
// and return the signed transaction
const tx = {
from: transaction.from,
to: transaction.to,
value: transaction.value,
gas: transaction.gasLimit?.toString(),
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas?.toString(),
maxFeePerGas: transaction.maxFeePerGas?.toString(),
nonce: transaction.nonce,
data: transaction.data,
}
const signedTransaction = await callJsonRpcEndpoint(
this.remoteSigningEndpoint,
"eth_signTransaction",
[tx]
);

return signedTransaction;
}

connect(provider: providers.Provider): Signer {
return new RemoteSigner(this.address, provider, this.remoteSigningEndpoint);
}

private async signMessageHash(messageHash: string): Promise<string> {
// Implement the logic to send the message hash to your remote signing service
// and return the signature
const signature = await callJsonRpcEndpoint(
this.remoteSigningEndpoint,
"eth_sign",
[this.address, messageHash]
);

return signature;
}
}

interface JsonRpcRequest {
jsonrpc: string;
method: string;
params: any[];
id: number;
}

interface JsonRpcResponse {
jsonrpc: string;
result?: any;
error?: {
code: number;
message: string;
};
id: number;
}

async function callJsonRpcEndpoint(
url: string,
method: string,
params: any[] = []
): Promise<any> {
const request: JsonRpcRequest = {
jsonrpc: "2.0",
method,
params,
id: 1, // You might want to generate a unique id for each request
};

const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(request),
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const jsonResponse: JsonRpcResponse = await response.json();

if (jsonResponse.error) {
throw new Error(`JSON-RPC error: ${jsonResponse.error.message}`);
}

return jsonResponse.result;
}

0 comments on commit 99542ec

Please sign in to comment.