zksync-sso
Version:
ZKsync Smart Sign On SDK
150 lines (136 loc) • 5.03 kB
text/typescript
import { type Account, assertRequest, type AssertRequestErrorType, type Chain, type Client, type ExactPartial, type Hash, type PublicActions, type RpcSchema, type SendTransactionRequest, type Transport, type WalletActions } from "viem";
import { parseAccount } from "viem/accounts";
import { prepareTransactionRequest, sendRawTransaction } from "viem/actions";
import { type ChainEIP712, type SendEip712TransactionParameters, type SendEip712TransactionReturnType, type SendTransactionParameters, zksync } from "viem/zksync";
import { InvalidEip712TransactionError, type InvalidEip712TransactionErrorType } from "../../utils/assertEip712Transaction.js";
import { isEIP712Transaction } from "../../utils/isEip712Transaction.js";
/**
* Creates, signs, and sends a new EIP712 transaction to the network.
*
* @param client - Client to use
* @param parameters - {@link SendEip712TransactionParameters}
* @returns The [Transaction](https://viem.sh/docs/glossary/terms#transaction) hash. {@link SendTransactionReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { zksync } from 'viem/chains'
* import { sendEip712Transaction } from 'viem/zksync'
*
* const client = createWalletClient({
* chain: zksync,
* transport: custom(window.ethereum),
* })
* const hash = await sendEip712Transaction(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 1000000000000000000n,
* })
*
* @example
* // Account Hoisting
* import { createWalletClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { zksync } from 'viem/chains'
* import { sendEip712Transaction } from 'viem/zksync'
*
* const client = createWalletClient({
* account: privateKeyToAccount('0x…'),
* chain: zksync,
* transport: http(),
* })
*
* const hash = await sendEip712Transaction(client, {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 1000000000000000000n,
* })
*/
export async function sendEip712Transaction<
chain extends ChainEIP712 | undefined,
account extends Account | undefined,
const request extends SendTransactionRequest<chain, chainOverride>,
chainOverride extends ChainEIP712 | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: SendEip712TransactionParameters<
chain,
account,
chainOverride,
request
>,
): Promise<SendEip712TransactionReturnType> {
const {
account: account_ = client.account,
chain = client.chain,
...rest
} = parameters;
if (!account_)
throw new Error("Account not found.");
const account = parseAccount(account_);
assertEip712Request(parameters);
const request = await getAction(
client,
prepareTransactionRequest,
"prepareTransactionRequest",
)({
account,
chain,
nonceManager: account.nonceManager,
...rest,
} as any);
const serializer = chain?.serializers?.transaction;
const serializedTransaction = (await account.signTransaction!(request, {
serializer,
})) as Hash;
return await getAction(
client,
sendRawTransaction,
"sendRawTransaction",
)({
serializedTransaction,
});
}
/**
* Retrieves and returns an action from the client (if exists), and falls
* back to the tree-shakable action.
*
* Useful for extracting overridden actions from a client (ie. if a consumer
* wants to override the `sendTransaction` implementation).
*/
export function getAction<
transport extends Transport,
chain extends Chain | undefined,
account extends Account | undefined,
rpcSchema extends RpcSchema | undefined,
extended extends { [key: string]: unknown },
client extends Client<transport, chain, account, rpcSchema, extended>,
parameters,
returnType,
>(
client: client,
actionFn: (_: any, parameters: parameters) => returnType,
// cspell:ignore minifiers
// Some minifiers drop `Function.prototype.name`, or replace it with short letters,
// meaning that `actionFn.name` will not always work. For that case, the consumer
// needs to pass the name explicitly.
name: keyof PublicActions | keyof WalletActions | (string & {}),
): (parameters: parameters) => returnType {
const action_implicit = client[actionFn.name];
if (typeof action_implicit === "function")
return action_implicit as (params: parameters) => returnType;
const action_explicit = client[name];
if (typeof action_explicit === "function")
return action_explicit as (params: parameters) => returnType;
return (params) => actionFn(client, params);
}
type AssertEip712RequestParameters = ExactPartial<
SendTransactionParameters<typeof zksync>
>;
/** @internal */
export type AssertEip712RequestErrorType =
| InvalidEip712TransactionErrorType
| AssertRequestErrorType;
export function assertEip712Request(args: AssertEip712RequestParameters) {
if (!isEIP712Transaction(args as any))
throw new InvalidEip712TransactionError();
assertRequest(args as any);
}