Signature Builders
Signature builders are specialized classes that construct and sign EVVM messages for different services. Each builder handles the specific requirements and patterns for its respective service.
Available Signature Builders
EVVMSignatureBuilder
Handles core EVVM payment operations:
signPay- Individual payments to addresses or usernamessignDispersePay- Multiple recipient payments in single transaction
NameServiceSignatureBuilder
Handles identity management operations:
signPreRegistrationUsername- Pre-register username with hashsignRegistrationUsername- Register username with paymentsignMakeOffer- Make offer for existing usernamesignWithdrawOffer- Withdraw previously made offersignAcceptOffer- Accept offer for your usernamesignRenewUsername- Renew username registrationsignAddCustomMetadata- Add custom metadata to identitysignRemoveCustomMetadata- Remove specific metadata entrysignFlushCustomMetadata- Remove all metadata for identitysignFlushUsername- Remove username completely
StakingSignatureBuilder
Handles staking operations:
signGoldenStaking- Golden staking (single signature, 5083 EVVM per stake)signPresaleStaking- Presale staking (dual signature with payment)signPublicStaking- Public staking (dual signature, flexible amounts)
GenericSignatureBuilder
Handles custom service operations:
- Generic message construction for any EVVM function
- Future-proofing for new services
Basic Usage Examples
1. Simple Payment (EVVMSignatureBuilder)
Basic payment signature for sending tokens or native currency:
import { EVVMSignatureBuilder, PayInputData } from '@evvm/viem-signature-library';
const createPaymentSignature = async () => {
const walletData = await getAccountWithRetry(config);
const walletClient = await getWalletClient(config);
const signatureBuilder = new EVVMSignatureBuilder(walletClient, walletData);
const signature = await signatureBuilder.signPay(
BigInt("1"), // evvmID
"alice.evvm", // recipient (address or username)
"0x0000000000000000000000000000000000000000" as `0x${string}`, // Native token
BigInt("1000000000000000000"), // 1 ETH
BigInt("50000000000000000"), // Priority fee
BigInt("123"), // Nonce
false, // Priority flag
"0x742d35Cc6634C0532925a3b8D138068fd4C1B7a1" as `0x${string}` // Executor
);
return signature;
};
2. Service + Payment (StakingSignatureBuilder)
Dual signature example - service function that requires payment:
import { StakingSignatureBuilder } from '@evvm/viem-signature-library';
const createPublicStakingSignature = async () => {
const walletData = await getAccountWithRetry(config);
const walletClient = await getWalletClient(config);
const stakingBuilder = new StakingSignatureBuilder(walletClient, walletData);
// Generate dual signatures (service + payment)
const { paySignature, actionSignature } = await stakingBuilder.signPublicStaking(
BigInt("1"), // evvmID
"0xStakingAddress" as `0x${string}`,
true, // isStaking
BigInt("5"), // amount of MATE tokens
BigInt("100"), // service nonce
BigInt("25415000000000000000000"), // total price (5 * 5083 EVVM)
BigInt("10000000000000000"), // priority fee
BigInt("101"), // payment nonce
false // priority flag
);
return { paySignature, actionSignature };
};
GenericSignatureBuilder Usage
Custom Service Functions
For new or custom EVVM services:
import { GenericSignatureBuilder } from '@evvm/viem-signature-library';
const createCustomServiceSignature = async () => {
const walletData = await getAccountWithRetry(config);
const walletClient = await getWalletClient(config);
const genericBuilder = new GenericSignatureBuilder(walletClient, walletData);
// Example: Coffee shop service
const serviceData = {
coffeeType: "Fisher Espresso",
quantity: BigInt(2),
totalPrice: BigInt("2000000000000000"), // 0.002 ETH
nonce: BigInt("42")
};
// Construct message parameters
const messageParams = [
serviceData.coffeeType,
serviceData.quantity.toString(),
serviceData.totalPrice.toString(),
serviceData.nonce.toString()
].join(",");
// Generate signature
const signature = await genericBuilder.signGenericMessage(
BigInt("1"), // evvmID
"orderCoffee", // Function name
messageParams // Parameters
);
// For custom services, you also need a payment signature
const evvmBuilder = new EVVMSignatureBuilder(walletClient, walletData);
const paymentSignature = await evvmBuilder.signPay(
BigInt("1"),
"0xCafeServiceAddress" as `0x${string}`,
"0x0000000000000000000000000000000000000000" as `0x${string}`,
serviceData.totalPrice,
serviceData.totalPrice / BigInt(1000), // Priority fee (0.1% of price)
BigInt("43"), // Different nonce for payment
false,
"0xCafeServiceAddress" as `0x${string}`
);
return {
serviceSignature: signature,
paymentSignature: paymentSignature,
serviceData
};
};
Common Patterns and Best Practices
Nonce Management
// Sync nonces (priority: false)
const getSyncNonce = async (userAddress: string) => {
return readContract(config, {
abi: EvvmABI,
address: evvmAddress,
functionName: 'getNextCurrentSyncNonce',
args: [userAddress],
});
};
// Async nonces (priority: true) - use random numbers
const generateAsyncNonce = () => {
return BigInt(Math.floor(Math.random() * 1000000));
};
Error Handling
const safeSignatureCreation = async () => {
try {
const signature = await signatureBuilder.signPay(/* parameters */);
return signature;
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('User rejected')) {
throw new Error('User cancelled signature request');
} else if (error.message.includes('insufficient funds')) {
throw new Error('Insufficient balance for gas fees');
}
}
throw new Error('Failed to create signature');
}
};
Type Safety
// Use proper typing for all inputs
interface PaymentFormData {
evvmID: string;
to: string;
tokenAddress: `0x${string}`;
amount: string;
priorityFee: string;
nonce: string;
priorityFlag: boolean;
executor: `0x${string}`;
}
// Validate addresses before signing
const isValidAddress = (address: string): address is `0x${string}` => {
return /^0x[a-fA-F0-9]{40}$/.test(address);
};
Address vs Username Handling
const handleRecipient = (input: string) => {
if (input.startsWith('0x')) {
// Address payment
return {
to_address: input as `0x${string}`,
to_identity: ""
};
} else {
// Username payment
return {
to_address: "0x0000000000000000000000000000000000000000" as `0x${string}`,
to_identity: input
};
}
};
Integration with React Components
Form State Management
const PaymentForm = () => {
const [formData, setFormData] = useState<PaymentFormData>({
evvmID: "1",
to: "",
tokenAddress: "0x0000000000000000000000000000000000000000",
amount: "",
priorityFee: "",
nonce: "",
priorityFlag: false,
executor: "0x0000000000000000000000000000000000000000"
});
const [payInputData, setPayInputData] = useState<PayInputData | null>(null);
const handleSignature = async () => {
const result = await createPaymentSignature();
setPayInputData(result);
};
return (
<form onSubmit={handleSignature}>
{/* Form inputs */}
<button type="submit">Create Signature</button>
{payInputData && <PaymentReceipt data={payInputData} />}
</form>
);
};