From 0ca3824e0952e2de78319abb254ab121a81d51ad Mon Sep 17 00:00:00 2001 From: RnkSngh Date: Thu, 7 Nov 2024 23:07:09 -0500 Subject: [PATCH] add txServiceUrl in safe config --- package.json | 2 +- src/evm/schemas/multisig.ts | 3 + src/multisig/safe.ts | 112 +++++++++++++++++++---------- src/scripts/execute-multisig-tx.ts | 6 +- src/tx.ts | 4 +- src/utils/io.ts | 2 +- 6 files changed, 82 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index 2596d4b5..6f7a68f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@open-ibc/vibc-core-smart-contracts", - "version": "4.0.9", + "version": "4.0.10", "main": "dist/index.js", "bin": { "verify-vibc-core-smart-contracts": "./dist/scripts/verify-contract-script.js", diff --git a/src/evm/schemas/multisig.ts b/src/evm/schemas/multisig.ts index 1a0bceef..502123c9 100644 --- a/src/evm/schemas/multisig.ts +++ b/src/evm/schemas/multisig.ts @@ -8,6 +8,7 @@ export const uninitializedMultisigConfig = z privateKey: z.string().min(1), owners: z.array(z.string().min(1)), threshold: z.number(), + txServiceUrl: z.string().optional() }) .strict(); @@ -18,6 +19,7 @@ export const initializedMultisigConfig = z chainId: z.number(), privateKey: z.string().min(1), safeAddress: z.string().min(1), + txServiceUrl: z.string().optional(), }) .strict(); @@ -37,6 +39,7 @@ export const initializedMultisig = z.object({ privateKey: z.string().min(1), safeAddress: z.string().min(1), wallet: wallet, + txServiceUrl: z.string().optional(), }); export type UninitializedMultisigConfig = z.infer< diff --git a/src/multisig/safe.ts b/src/multisig/safe.ts index 67f87ec3..c09728ba 100644 --- a/src/multisig/safe.ts +++ b/src/multisig/safe.ts @@ -1,11 +1,11 @@ import SafeApiKit from "@safe-global/api-kit"; import Safe, { SafeFactory } from "@safe-global/protocol-kit"; import { + MetaTransactionData, OperationType, - SafeTransactionDataPartial, TransactionResult, } from "@safe-global/safe-core-sdk-types"; -import { ethers } from "ethers"; +import { InitializedMultisig } from "../evm/schemas/multisig"; /* * Init a safe owner from a given private key to a give RPC's network @@ -37,36 +37,31 @@ export const newSafeFromOwner = async ( * @param data Propose a transaction from a signer account */ export async function proposeTransaction( - safeAddress: string, + initializedMultisig: InitializedMultisig, toAddress: string, txData: string, - proposerPrivateKey: string, - chainId: bigint, rpcUrl: string, nonce: number, value = "0", operation = OperationType.Call ) { - const apiKit = new SafeApiKit({ chainId }); + console.log( + `proposing transaction from \n \n @safe ${initializedMultisig.safeAddress} to ${toAddress} with value: ${value} and data: ${txData}, and nonce : ${nonce}` + ); const txProposer = await Safe.init({ provider: rpcUrl, - signer: proposerPrivateKey, - safeAddress: safeAddress, + signer: initializedMultisig.wallet.privateKey, + safeAddress: initializedMultisig.safeAddress, }); - console.log( - `proposing transaction from ${txProposer} @safe ${safeAddress} to ${toAddress} with value: ${value} and data: ${txData}, and nonce : ${nonce}` - ); - - const txProposerAddress = new ethers.Wallet(proposerPrivateKey).address; + const txProposerAddress = initializedMultisig.wallet.address; - const safeTransactionData: SafeTransactionDataPartial = { + const safeTransactionData: MetaTransactionData = { to: toAddress, value, data: txData, operation: operation, - nonce, }; const safeTransaction = await txProposer.createTransaction({ @@ -76,43 +71,86 @@ export async function proposeTransaction( const safeTxHash = await txProposer.getTransactionHash(safeTransaction); const proposerSignature = await txProposer.signHash(safeTxHash); - - // Propose transaction to the service - await apiKit.proposeTransaction({ - safeAddress, - safeTransactionData: safeTransaction.data, - safeTxHash, - senderAddress: txProposerAddress, - senderSignature: proposerSignature.data, - }); - - console.log( - `Transaction has been proposed with hash: ${safeTxHash} from address ${txProposerAddress}` - ); - return await apiKit.getTransaction(safeTxHash); + if (initializedMultisig.txServiceUrl) { + // For custom tx services, we have to submit our own http request since it seems like the safeApiKit.proposeTransaction method isn't compatible with some tx service urls + + const url = `${initializedMultisig.txServiceUrl}/v1/chains/${initializedMultisig.chainId}/transactions/${initializedMultisig.safeAddress}/propose`; + const body = JSON.stringify({ + ...safeTransaction.data, + nonce: nonce.toString(), + signature: proposerSignature.data, + sender: txProposerAddress, + safeTxHash, + }); + + console.log(`proposing transaction to tx service:${url} \n `); + console.log(`with body${body}`); + try { + const res = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body, + }); + + const json = await res.json(); + + if (!res.ok) { + console.log( + `received non 200 response from tx service ${res.status} ${json.message}` + ); + } + return json; + } catch (e) { + console.log( + `error sending pending multisig tx to tx service ${initializedMultisig.txServiceUrl}`, + e + ); + + return; + } + } else { + const apiKit = new SafeApiKit({ + chainId: BigInt(initializedMultisig.chainId), + }); + // Propose transaction to the service + await apiKit.proposeTransaction({ + safeAddress: initializedMultisig.safeAddress, + safeTransactionData: safeTransaction.data, + safeTxHash, + senderAddress: txProposerAddress, + senderSignature: proposerSignature.data, + }); + console.log( + `Transaction has been proposed with hash: ${safeTxHash} from address ${txProposerAddress}` + ); + return await apiKit.getTransaction(safeTxHash); + } } /** * Execute a multisig tx from an account generated from proposeTransaction */ export const executeMultisigTx = async ( - safeAddress: string, - executorPrivateKey: string, - chainId: bigint, + initializedMultisig: InitializedMultisig, rpcUrl: string, pendingTransactionIndex: number -):Promise => { +): Promise => { const apiKit = new SafeApiKit({ - chainId, + chainId: BigInt(initializedMultisig.chainId), + txServiceUrl: initializedMultisig.txServiceUrl, }); const executor = await Safe.init({ provider: rpcUrl, - signer: executorPrivateKey, - safeAddress, + signer: initializedMultisig.wallet.privateKey, + safeAddress: initializedMultisig.safeAddress, }); - const transactions = await apiKit.getPendingTransactions(safeAddress); + const transactions = await apiKit.getPendingTransactions( + initializedMultisig.safeAddress + ); if (transactions.results.length <= pendingTransactionIndex) { throw new Error( diff --git a/src/scripts/execute-multisig-tx.ts b/src/scripts/execute-multisig-tx.ts index df9c7473..6d94f1e5 100644 --- a/src/scripts/execute-multisig-tx.ts +++ b/src/scripts/execute-multisig-tx.ts @@ -20,12 +20,8 @@ async function main() { throw new Error("Can only execute transactions on a multisig wallet"); } - const privateKey = accounts.getSinglePrivateKeyFromAccount(executor); - executeMultisigTx( - multisigAccount.safeAddress, - privateKey, - BigInt(multisigAccount.chainId), + multisigAccount, rpcUrl, txIndex ); diff --git a/src/tx.ts b/src/tx.ts index 1bed5293..0337b768 100644 --- a/src/tx.ts +++ b/src/tx.ts @@ -76,11 +76,9 @@ export async function sendTx( ); proposeTransaction( - account.safeAddress, + account, deployedContractAddress, callData, - account.wallet.privateKey, - BigInt(chain.chainId), chain.rpc, updatedNonces[account.safeAddress] ); diff --git a/src/utils/io.ts b/src/utils/io.ts index 9b09e705..f4cbec40 100644 --- a/src/utils/io.ts +++ b/src/utils/io.ts @@ -516,7 +516,6 @@ export const saveMultisigAddressToAccountsSpec = async ( accountsSpecPath: string, chainId: number, ownerName: string // Used to find which owner to write to, - ) => { // TODO: Currently this yaml lib doesn't include comments - we need to figure out a way to preserve comments / whitespaces, etc const yamlFile = readYamlFile(accountsSpecPath); @@ -532,6 +531,7 @@ export const saveMultisigAddressToAccountsSpec = async ( chainId, safeAddress: newSafeAddress, privateKey: account.privateKey, + txServiceUrl: account.txServiceUrl }; });