Custodial account transactions are unique as these users cannot sign transactions using their web3 wallets. To ensure security and align with the characteristics of web3 transactions, we have developed a special application called the Custodial Signer. This application securely handles the transaction signing process by communicating with a server specifically designed to manage custodial account transactions.
Integration Guide
To use custodial accounts to complete transactions, requires users to implement a popup window and communicate with the custodial signer via post messages to obtain the signature. Then, the obtained signature is used to send the transaction to the blockchain.
Code Example: Barebones Solution
Step 1: Get Signature by Communicating with Custodial Signer
import { ethers } from 'ethers'
const rawTransactionWithoutSignature = {
to: 'the destination wallet address',
value: ethers.parseEther('0.01'),
chainId: 'the chain id',
gasLimit: 210000,
gasPrice: ethers.parseUnits('10.0', 'gwei'),
}
let nonce = 0
const custodialSignerUrl = 'the custodial signer service url'
async function signTransaction() {
if (typeof window === 'undefined') {
return
}
const fromAccount = 'your own wallet address'
const transactionCount = await provider.getTransactionCount(fromAccount)
nonce = transactionCount + 1
const serializedUnsignedTransaction = ethers.Transaction.from({
...rawTransactionWithoutSignature,
nonce,
}).unsignedSerialized
const signTransactionPayload = {
account: fromAccount,
transaction: serializedUnsignedTransaction,
}
// If using native clients (games) payload can include callbackUrl to which signature will be sent
// const signTransactionPayload = {
// account: fromAccount,
// transaction: serializedUnsignedTransaction,
// callbackUrl: 'http://localhost:3000/signature-callback' // <- your game callback URL here
// };
const id = 'client:2' // must be formatted as `client:${ an identifier number }`
const tag = 'fv/sign-tx' // do not change this
const encodedPayload = {
id,
tag,
payload: signTransactionPayload,
}
window.open(
`${custodialSignerUrl}?request=${base64UrlEncode(
JSON.stringify(encodedPayload),
)}`,
'futureverse_wallet', // don't change this
'popup,right=0,width=290,height=286,menubar=no,toolbar=no,location=no,status=0',
)
window.addEventListener('message', (ev) => {
if (ev.origin === custodialSignerUrl) {
const dataR = signMessageType.decode(ev.data)
if (E.isRight(dataR)) {
transactionSignature = dataR.right.payload.response.signature
}
}
})
}
Refer: getSignatureFromCustodialSigner to get signature on web and native(server) clients.
Step 2: Send Transaction to Blockchain
sync function sendTransaction() {
if (transactionSignature == null || fromAccount == null) {
return;
}
const rawTransactionWithSignature = {
...rawTransactionWithoutSignature,
signature: transactionSignature,
from: fromAccount,
nonce,
};
const serializedSignedTransaction = ethers.Transaction.from(
rawTransactionWithSignature
).serialized;
const transactionResponse = await provider.broadcastTransaction(
serializedSignedTransaction
);
}
Refer: transactions for signEthTransaction, signRootTransaction and sendTransaction.