Skip to content

Commit

Permalink
Viem: Multisend (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
bh2smith authored Sep 16, 2024
1 parent 89f2ccd commit adcfb30
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 88 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"@safe-global/safe-deployments": "^1.37.0",
"@safe-global/safe-modules-deployments": "^2.2.0",
"ethers": "^6.13.1",
"ethers-multisend": "^3.1.0",
"near-api-js": "^5.0.0",
"near-ca": "^0.5.2",
"viem": "^2.16.5",
Expand All @@ -64,4 +63,4 @@
"typescript": "^5.5.2",
"yargs": "^17.7.2"
}
}
}
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ export * from "./tx-manager";
export * from "./types";
export * from "./util";

export { MetaTransaction } from "ethers-multisend";
export { Network, BaseTx, SignRequestData, populateTx } from "near-ca";
59 changes: 59 additions & 0 deletions src/lib/multisend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
Address,
encodeFunctionData,
encodePacked,
Hex,
parseAbi,
size,
} from "viem";
import { MetaTransaction, OperationType } from "../types";

export const MULTI_SEND_ABI = ["function multiSend(bytes memory transactions)"];

const MULTISEND_141 = "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526";
const MULTISEND_CALLONLY_141 = "0x9641d764fc13c8B624c04430C7356C1C7C8102e2";

/// Encodes the transaction as packed bytes of:
/// - `operation` as a `uint8` with `0` for a `call` or `1` for a `delegatecall` (=> 1 byte),
/// - `to` as an `address` (=> 20 bytes),
/// - `value` as a `uint256` (=> 32 bytes),
/// - length of `data` as a `uint256` (=> 32 bytes),
/// - `data` as `bytes`.
const encodeMetaTx = (tx: MetaTransaction): Hex =>
encodePacked(
["uint8", "address", "uint256", "uint256", "bytes"],
[
tx.operation || OperationType.Call,
tx.to as Address,
BigInt(tx.value),
BigInt(size(tx.data as Hex)),
tx.data as Hex,
]
);

const remove0x = (hexString: Hex) => hexString.slice(2);

Check failure on line 34 in src/lib/multisend.ts

View workflow job for this annotation

GitHub Actions / types

Missing return type on function

// Encodes a batch of module transactions into a single multiSend module transaction.
// A module transaction is an object with fields corresponding to a Gnosis Safe's (i.e., Zodiac IAvatar's) `execTransactionFromModule` method parameters.
export function encodeMulti(
transactions: readonly MetaTransaction[],
multiSendContractAddress: string = transactions.some(
(t) => t.operation === OperationType.DelegateCall
)
? MULTISEND_141
: MULTISEND_CALLONLY_141
): MetaTransaction {
const encodedTransactions =
"0x" + transactions.map(encodeMetaTx).map(remove0x).join("");

return {
operation: OperationType.DelegateCall,
to: multiSendContractAddress,
value: "0x00",
data: encodeFunctionData({
abi: parseAbi(MULTI_SEND_ABI),
functionName: "multiSend",
args: [encodedTransactions],
}),
};
}
8 changes: 6 additions & 2 deletions src/lib/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import {
getSafeModuleSetupDeployment,
} from "@safe-global/safe-modules-deployments";
import { PLACEHOLDER_SIG, packGas, packPaymasterData } from "../util";
import { GasPrice, UnsignedUserOperation, UserOperation } from "../types";
import { MetaTransaction } from "ethers-multisend";
import {
GasPrice,
MetaTransaction,
UnsignedUserOperation,
UserOperation,
} from "../types";
import { Address, Hash, Hex } from "viem";

/**
Expand Down
4 changes: 2 additions & 2 deletions src/tx-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {
} from "near-ca";
import { Erc4337Bundler } from "./lib/bundler";
import { packSignature } from "./util";
import { UserOperation, UserOperationReceipt } from "./types";
import { MetaTransaction, encodeMulti } from "ethers-multisend";
import { MetaTransaction, UserOperation, UserOperationReceipt } from "./types";
import { ContractSuite } from "./lib/safe";
import { Address, Hash, Hex, serializeSignature } from "viem";
import { FinalExecutionOutcome } from "near-api-js/lib/providers";
import { encodeMulti } from "./lib/multisend";

export class TransactionManager {
readonly nearAdapter: NearEthAdapter;
Expand Down
12 changes: 12 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,15 @@ export interface GasPrice {
maxFeePerGas: Hex;
maxPriorityFeePerGas: Hex;
}

export enum OperationType {
Call = 0,
DelegateCall = 1,
}

export interface MetaTransaction {
readonly to: string;
readonly value: string;
readonly data: string;
readonly operation?: OperationType;
}
3 changes: 1 addition & 2 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { PaymasterData } from "./types.js";
import { MetaTransaction } from "ethers-multisend";
import { PaymasterData, MetaTransaction } from "./types.js";
import { Hex, concatHex, encodePacked, toHex } from "viem";

export const PLACEHOLDER_SIG = encodePacked(["uint48", "uint48"], [0, 0]);
Expand Down
22 changes: 22 additions & 0 deletions tests/lib.multisend.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { encodeMulti } from "../src/lib/multisend";

describe("Multisend", () => {
it("encodeMulti", () => {
const tx1 = {
to: "0x0000000000000000000000000000000000000001",
value: "1",
data: "0x12",
};
const tx2 = {
to: "0x0000000000000000000000000000000000000002",
value: "2",
data: "0x34",
};
expect(encodeMulti([tx1, tx2])).toEqual({
data: "0x8d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000011200000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000",
operation: 1,
to: "0x9641d764fc13c8B624c04430C7356C1C7C8102e2",
value: "0x00",
});
});
});
83 changes: 4 additions & 79 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -474,22 +474,7 @@
dependencies:
levn "^0.4.1"

"@ethersproject/abi@^5.0.0", "@ethersproject/abi@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449"
integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==
dependencies:
"@ethersproject/address" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/hash" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"

"@ethersproject/abstract-provider@^5.5.1", "@ethersproject/abstract-provider@^5.7.0":
"@ethersproject/abstract-provider@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef"
integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==
Expand All @@ -513,7 +498,7 @@
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"

"@ethersproject/address@^5.0.0", "@ethersproject/address@^5.7.0":
"@ethersproject/address@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37"
integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==
Expand All @@ -531,7 +516,7 @@
dependencies:
"@ethersproject/bytes" "^5.7.0"

"@ethersproject/bignumber@^5.0.0", "@ethersproject/bignumber@^5.7.0":
"@ethersproject/bignumber@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2"
integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==
Expand All @@ -540,7 +525,7 @@
"@ethersproject/logger" "^5.7.0"
bn.js "^5.2.1"

"@ethersproject/bytes@^5.0.0", "@ethersproject/bytes@^5.7.0":
"@ethersproject/bytes@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d"
integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==
Expand All @@ -554,22 +539,6 @@
dependencies:
"@ethersproject/bignumber" "^5.7.0"

"@ethersproject/contracts@^5.5.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e"
integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==
dependencies:
"@ethersproject/abi" "^5.7.0"
"@ethersproject/abstract-provider" "^5.7.0"
"@ethersproject/abstract-signer" "^5.7.0"
"@ethersproject/address" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/transactions" "^5.7.0"

"@ethersproject/hash@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7"
Expand Down Expand Up @@ -620,15 +589,6 @@
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"

"@ethersproject/sha2@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb"
integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
hash.js "1.1.7"

"@ethersproject/signing-key@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3"
Expand All @@ -641,18 +601,6 @@
elliptic "6.5.4"
hash.js "1.1.7"

"@ethersproject/solidity@^5.0.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8"
integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/sha2" "^5.7.0"
"@ethersproject/strings" "^5.7.0"

"@ethersproject/strings@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2"
Expand All @@ -677,15 +625,6 @@
"@ethersproject/rlp" "^5.7.0"
"@ethersproject/signing-key" "^5.7.0"

"@ethersproject/units@^5.0.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1"
integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/logger" "^5.7.0"

"@ethersproject/web@^5.7.0":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae"
Expand Down Expand Up @@ -2543,20 +2482,6 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==

ethers-multisend@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/ethers-multisend/-/ethers-multisend-3.1.0.tgz#76d6743904250c8f172635301732fc7e41b48a7b"
integrity sha512-PVfe4v9vs96cAages4yMoEAagOw0Hdh3fHe8IyMnLPNgV8KcGOY1Kv4NYnwB3t7EilnZbQ4jw3Nx0r+1Dc09kw==
dependencies:
"@ethersproject/abi" "^5.0.0"
"@ethersproject/abstract-provider" "^5.5.1"
"@ethersproject/address" "^5.0.0"
"@ethersproject/bignumber" "^5.0.0"
"@ethersproject/bytes" "^5.0.0"
"@ethersproject/contracts" "^5.5.0"
"@ethersproject/solidity" "^5.0.0"
"@ethersproject/units" "^5.0.0"

ethers@^6.13.1:
version "6.13.2"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.2.tgz#4b67d4b49e69b59893931a032560999e5e4419fe"
Expand Down

0 comments on commit adcfb30

Please sign in to comment.