Using Smart Wallets
To request signatures or transactions from a connected wallet, you can either:
- Use the wallet's EIP-1193 provider to send JSON-RPC requests to the wallet directly.
- Pass the wallet to a library like
viem
,ethers
, orwagmi
. - For the embedded wallet specifically, create a
transactionIntent
from the server andsignMessage
it with the embedded wallet (client).
Signatures
Choose your preferred approach for handling wallet signatures:
Signing Messages with EIP-1193 Provider
The request
method of the EIP-1193 provider can be used to request signatures. First, get the provider:
import openfort from "./openfortConfig"
// This example assumes you have already checked that Openfort 'embeddedState' is
// `ready` and the user is `authenticated`
const provider = openfort.embeddedWallet.getEthereumProvider();
import Openfort from '@openfort/openfort-js';
const openfort = new Openfort({
baseConfiguration: {
publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
},
shieldConfiguration: {
shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
},
});
export default openfort;
Then use the provider's request
method with eth_sign
or related methods:
const message = "Sign this message";
const signature = await provider.request({
method: 'personal_sign',
params: [message, address]
});
Signing Typed Data with EIP-1193 Provider
The request
method of the EIP-1193 provider can be used to sign typed data. First, get the provider:
import openfort from "./openfortConfig"
// This example assumes you have already checked that Openfort 'embeddedState' is
// `ready` and the user is `authenticated`
const provider = openfort.embeddedWallet.getEthereumProvider();
Then use the provider's request
method with eth_signTypedData_v4
:
const address = "0x0000000000000000000000000000000000000000";
const domain = {
name: "Ether Mail",
version: "1",
chainId: 1,
verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
};
const types = {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" },
],
Person: [
{ name: "name", type: "string" },
{ name: "wallet", type: "address" },
],
Mail: [
{ name: "from", type: "Person" },
{ name: "to", type: "Person" },
{ name: "contents", type: "string" },
],
};
const message = {
from: { name: "Cow", wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" },
to: { name: "Bob", wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" },
contents: "Hello, Bob!",
};
const signature = await provider.request({ method: "eth_signTypedData_v4",
params: [address, JSON.stringify({ domain, types, primaryType: "Mail", message })],
});
Integration with Web3 Libraries
If you've integrated Openfort with another library, you can also use that library's syntax for requesting signatures:
Library | Method |
---|---|
Ethers | Use the signer's signMessage method |
Wagmi | Use the useSignMessage hook |
Viem | Use the signMessage action |
For typed data:
Library | Method |
---|---|
Ethers | Use the signer's _signTypedData method |
Wagmi | Use the useSignTypedData hook |
Viem | Use the signTypedData action |
Signing Messages with Openfort SDK
To request a signature from a user, use the signMessage
method.
When invoked, signMessage
will request an EIP-191 personal_sign signature from the embedded wallet, and returns a Promise for the user's signature as a string.
import openfort from "./openfortConfig"
// This example assumes you have already checked that Openfort 'embeddedState' is
// `ready` and the user is `authenticated`
async function signMessageButton(message:string) {
await openfort.embeddedWallet.signMessage(message);
}
import Openfort from '@openfort/openfort-js';
const openfort = new Openfort({
baseConfiguration: {
publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
},
shieldConfiguration: {
shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
},
});
export default openfort;
Signing Typed Data with Openfort SDK
To have a user sign an EIP-712 typed data signature, use the signTypedData
method.
When invoked, signTypedData
will request an EIP-721 eth_signTypedData_v4 signature from the embedded wallet, and returns a Promise for the user's signature as a string.
import openfort from "./openfortConfig"
// This example assumes you have already checked that Openfort 'embeddedState' is
// `ready` and the user is `authenticated`
async function signTypedMessageButton(domain: any, types: any, value: any) {
await openfort.embeddedWallet.signTypedData(domain, types, value);
}
Transactions
Choose your preferred approach for handling wallet transactions:
Native Transactions with EIP-1193 Provider
Get the provider and optionally configure gas policy to make a sponsored transaction:
import openfort from "./openfortConfig"
// This example assumes you have already checked that Openfort 'embeddedState' is
// `ready` and the user is `authenticated`
const provider = await openfort.embeddedWallet.getEthereumProvider({
policy: 'pol_...',
});
import Openfort from '@openfort/openfort-js';
const openfort = new Openfort({
baseConfiguration: {
publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
},
shieldConfiguration: {
shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
},
});
export default openfort;
Then, using the provider's request
method, send a eth_sendTransaction
JSON-RPC to the wallet:
const transactionRequest = {
to: '0xRecipientAddress',
value: 100000,
};
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [transactionRequest],
});
Smart Contract Interactions with EIP-1193 Provider
Calling a smart contract is a special case of sending a transaction. To call a smart contract, you should send a transaction with the following parameters:
to
: the address of the smart contract to calldata
: the encoded function call datavalue
: any value to send (for payable functions)
To prepare the calldata for a smart contract interaction, we recommend using viem's encodeFunctionData method:
import { encodeFunctionData } from 'viem';
const data = encodeFunctionData({
abi: contractAbi,
functionName: 'methodName',
args: [arg1, arg2]
});
You can then send a transaction from the wallet as normal, and pass the calldata in the data
field:
const transactionRequest = {
to: '0xTheContractAddress',
data: data,
value: 100000, // Only necessary for payable methods
};
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [transactionRequest]
});
Batched Transactions with EIP-1193 Provider
Smart wallets support sending a batch of transactions in a single, atomic submission to the network.
To send batched transactions with a smart wallet, call the wallet_sendCalls
method with a calls array of the transactions to batch together:
const txHash = await provider.request({
method: 'wallet_sendCalls',
params: [
{
calls: [
// Approve transaction
{
to: USDC_ADDRESS,
data: encodeFunctionData({
abi: USDC_ABI,
functionName: 'approve',
args: ['insert-spender-address', BigInt(1e6)],
}),
},
// Transfer transaction
{
to: USDC_ADDRESS,
data: encodeFunctionData({
abi: USDC_ABI,
functionName: 'transfer',
args: ['insert-recipient-address', BigInt(1e6)],
}),
},
],
},
],
});
Integration with Wallet Libraries
If you've integrated Openfort with another library, you can also use that library's syntax for sending transactions:
Library | Method |
---|---|
Ethers | Use the signer's sendTransaction method. |
Wagmi | Use the useSendTransaction hook. |
Viem | sendTransaction action |
For batched transactions:
Library | Method |
---|---|
Wagmi | Use the useWriteContracts hook. |
Viem | sendCalls action |
Transaction Flow with Openfort SDK
The Openfort SDK uses a two-step approach: create the transaction intent on the server, then sign it on the client.
1. Create Transaction Intent on Server
Create a request to your backend to create a transaction. In the body of the request:
- Include the
player
that signs the transaction. - Include the
policy
that interacts with the contract for gas. If non-existent the user will need to have gas tokens. - Include
optimistic
if you want the transactions to be confirmed faster.
Native Token Transfer
Send the value of native tokens in the smallest denomination of the native currency i.e. wei (10^18) and as a string
.
curl https://api.openfort.io/v1/transaction_intents \
-H "Authorization: Bearer $YOUR_SECRET_KEY" \
-d player="pla_..." \
-d policy="pol_..." \
-d chainId=80002 \
-d optimistic=true \
-d "interactions[0][value]=1000" \
-d "interactions[0][to]=pla_..."
For a useful resource for computing the Wei, you can visit the wei calculator.
Smart Contract Transactions
curl https://api.openfort.io/v1/transaction_intents \
-H "Authorization: Bearer $YOUR_SECRET_KEY" \
-d player="pla_..." \
-d policy="pol_..." \
-d chainId=80002 \
-d optimistic=true \
-d "interactions[0][contract]"="con_..." \
-d "interactions[0][functionName]"="mint" \
-d "interactions[0][functionArgs][0]"="0x63B7...484f"
Batching Transactions
Smart accounts support batching transactions, allowing multiple actions to be rolled into one. This feature significantly simplifies Web3 interactions for your users. For example, instead of executing approve()
and then transfer()
, your user can perform both in a single transaction.
For security reasons, there is a limit of 9 interactions per transaction intent.
curl https://api.openfort.io/v1/transaction_intents \
-H "Authorization: Bearer $YOUR_SECRET_KEY" \
-d player="pla_...", \
-d policy="pol_..." \
-d chainId=80002 \
-d optimistic=true \
-d "interactions[0][contract]=con_..." \
-d "interactions[0][functionName]=mint" \
-d "interactions[0][functionArgs][0]=0x63B7...484f" \
-d "interactions[1][contract]=con_..." \
-d "interactions[1][functionName]=transfer" \
-d "interactions[1][functionArgs][0]=0x32B7...213d"
2. Sign Transaction on Client
Use the nextAction
returned by the backend to sign the transaction with the embedded wallet.
The transaction will be automatically signed and broadcasted by using the sendSignatureTransactionIntentRequest
method.
const handleCollectButtonClick = async () => {
const collectResponse = await fetch(`https://your-backend.com/api/mint`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const collectResponseJSON = await collectResponse.json();
if (collectResponseJSON.data?.nextAction) {
// This example assumes you have already checked that Openfort 'embeddedState' is
// `ready` and the user is `authenticated`
const response = await openfort.sendSignatureTransactionIntentRequest(
collectResponseJSON.data.id,
collectResponseJSON.data.nextAction.payload.userOperationHash
);
console.log("response", response);
}
console.log("success:", collectResponseJSON.data);
};