@abstract-foundation/agw-client
Version:
Abstract Global Wallet Client SDK
130 lines • 5.45 kB
JavaScript
import { BaseError, encodeAbiParameters, parseAbiParameters, } from "viem";
import { getChainId, readContract, signTypedData } from "viem/actions";
import { assertCurrentChain, getAction, parseAccount } from "viem/utils";
import AGWAccountAbi from "../abis/AGWAccount.js";
import { assertEip712Request, } from "../eip712.js";
import { AccountNotFoundError } from "../errors/account.js";
import { assertSessionKeyPolicies } from "../sessionValidator.js";
import { transformHexValues, VALID_CHAINS } from "../utils.js";
import { signPrivyTransaction } from "./sendPrivyTransaction.js";
export async function signTransaction(client, signerClient, publicClient, args, validator, validationHookData = {}, customPaymasterHandler = undefined, isPrivyCrossApp = false) {
const chain = client.chain;
if (isPrivyCrossApp) {
return signPrivyTransaction(client, args);
}
if (!chain?.serializers?.transaction)
throw new BaseError("transaction serializer not found on chain.");
const { transaction, customSignature } = await signEip712TransactionInternal(client, signerClient, publicClient, args, validator, validationHookData, customPaymasterHandler);
return chain.serializers.transaction({
...transaction,
customSignature,
type: "eip712",
}, { r: "0x0", s: "0x0", v: 0n });
}
export async function signEip712TransactionInternal(client, signerClient, publicClient, args, validator, validationHookData = {}, customPaymasterHandler = undefined) {
// ensure the transaction type is set to eip712
args.type = "eip712";
const { account: account_ = client.account, chain = client.chain, ...transaction } = args;
transformHexValues(transaction, [
"value",
"nonce",
"maxFeePerGas",
"maxPriorityFeePerGas",
"gas",
"chainId",
"gasPerPubdata",
]);
if (!account_)
throw new AccountNotFoundError({
docsPath: "/docs/actions/wallet/signTransaction",
});
const smartAccount = parseAccount(account_);
const useSignerAddress = transaction.from === signerClient.account.address;
const fromAccount = useSignerAddress ? signerClient.account : smartAccount;
assertEip712Request({
account: fromAccount,
chain,
...transaction,
});
if (!chain || VALID_CHAINS[chain.id] === undefined) {
throw new BaseError("Invalid chain specified");
}
if (!chain?.custom?.getEip712Domain)
throw new BaseError("`getEip712Domain` not found on chain.");
const chainId = await getAction(client, getChainId, "getChainId")({});
if (chain !== null)
assertCurrentChain({
currentChainId: chainId,
chain: chain,
});
await assertSessionKeyPolicies(publicClient, chainId, fromAccount, transaction);
const transactionWithPaymaster = await getTransactionWithPaymasterData(chainId, fromAccount, transaction, customPaymasterHandler);
if (transactionWithPaymaster.data === undefined) {
// serializer turns undefined into 0x00 which causes issues sending
// eth to contracts that don't have a fallback function
transactionWithPaymaster.data = "0x";
}
const eip712Domain = chain?.custom.getEip712Domain({
...transactionWithPaymaster,
type: "eip712",
});
const rawSignature = await signTypedData(signerClient, {
...eip712Domain,
account: signerClient.account,
});
let signature;
if (useSignerAddress) {
signature = rawSignature;
}
else {
const hookData = [];
if (!useSignerAddress) {
const validationHooks = await getAction(client, readContract, "readContract")({
address: client.account.address,
abi: AGWAccountAbi,
functionName: "listHooks",
args: [true],
});
for (const hook of validationHooks) {
hookData.push(validationHookData[hook] ?? "0x");
}
}
// Match the expect signature format of the AGW smart account
signature = encodeAbiParameters(parseAbiParameters(["bytes", "address", "bytes[]"]), [rawSignature, validator, hookData]);
}
return {
transaction: transactionWithPaymaster,
customSignature: signature,
};
}
async function getTransactionWithPaymasterData(chainId, fromAccount, transaction, customPaymasterHandler = undefined) {
if (customPaymasterHandler &&
!transaction.paymaster &&
!transaction.paymasterInput) {
const paymasterResult = await customPaymasterHandler({
chainId,
from: fromAccount.address,
data: transaction.data,
gas: transaction.gas ?? 0n,
gasPrice: transaction.gasPrice ?? 0n,
gasPerPubdata: transaction.gasPerPubdata ?? 0n,
maxFeePerGas: transaction.maxFeePerGas ?? 0n,
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas ?? 0n,
nonce: transaction.nonce ?? 0,
to: transaction.to ?? "0x0",
value: transaction.value ?? 0n,
});
return {
...transaction,
...paymasterResult,
from: fromAccount.address,
chainId,
};
}
return {
...transaction,
from: fromAccount.address,
chainId,
};
}
//# sourceMappingURL=signTransaction.js.map