How to Write Your Smart Contract for Wert Widget
When users purchase crypto via Wert, our Proxy contract executes your smart contract in a single atomic transaction. To ensure your smart contract functions seamlessly with Wert, its logic and function parameters must be structured correctly.
High-Level Execution Overview
As shown in the diagram above, when the widget is initialized, it passes some of the parameters signed by signSmartContractData function to the Wert Proxy Contract, which executes your target contract function in a single transaction.
Payload Configuration for signSmartContractData function
Regardless of whether your contract accepts native coins or ERC-20 tokens, the backend payload configuration parameters remain identical:
Important Note:addressfield from signSmartContractData functionUser's wallet address. Used internally by Wert. It is not passed to our proxy contract nor your smart contract and does not route funds on-chain.
sc_address: Your deployed smart contract address called or approved by the Wert Proxy.commodity_amount: The value used by the Wert Proxy (for native coins, this is the exact value forwarded; for ERC-20s, this is the token allowance granted tosc_address).sc_input_data: The compiled data (function selector + encoded arguments) specifying which function to run on your contract and its corresponding function parameters values.
Important: Fund Routing Belongs insc_input_dataNotice that the address field is not part of the payload passed to your contract in the flow diagram | it is purely for Wert internally and does not transfer funds on-chain. All destination addresses (e.g., end-user recipients or merchant vaults) must be explicitly passed as function arguments inside
sc_input_data.
Integration Paths
Because fund routing mechanics differ fundamentally between native gas tokens and ERC-20 tokens, select the integration path below that corresponds to the asset type your smart contract accepts.
Path 1: Smart Contracts Accepting Native Coins (ETH, POL, BNB, etc.)
Native gas tokens do not have an approval mechanism. The Wert Proxy pushes native funds directly into your contract alongside the call.
Smart Contract Requirements
payableModifier Required: Your target function must be markedpayableto accept native funds.- Read
msg.value: The native funds sent by Wert arrive automatically inmsg.value. - Amount Parameter is Optional: Since native coins arrive via
msg.value, an explicitamountparameter insidesc_input_datais typically not needed, though you can include one if your specific logic requires it. - Explicit Recipient/Destination Parameters:
msg.senderinside your contract will be the Wert Proxy. Pass user or merchant destination addresses explicitly insidesc_input_data.
Contract Implementation Pattern Examples
Option A: Automatic Native Splitting (No amount Parameter)
Splits incoming msg.value directly between wallet1 and wallet2.
// Called via sc_input_data by Wert Proxy
function myPayableFunctionA(
address recipient, // End-user receiving the NFT/asset
address wallet1, // Primary recipient (e.g., merchant/treasury)
address wallet2, // Secondary recipient (e.g., fee collector)
uint256 wallet2FeeBps, // Fee in basis points (e.g., 250 = 2.5%)
uint256 itemId
) external payable {
// ... other logic
// Note: msg.value is automatically forwarded by Wert Proxy and will equal commodity_amount from signSmartContractData.
// Calculate split from incoming msg.value
uint256 wallet2Amount = (msg.value * wallet2FeeBps) / 10000;
uint256 wallet1Amount = msg.value - wallet2Amount;
// Send split portion to wallet2
if (wallet2Amount > 0) {
(bool success2, ) = payable(wallet2).call{value: wallet2Amount}("");
require(success2, "Transfer to wallet2 failed");
}
// Send remaining portion to wallet1
(bool success1, ) = payable(wallet1).call{value: wallet1Amount}("");
require(success1, "Transfer to wallet1 failed");
_mintItem(recipient, itemId);
// ... other logic
}Option B: Direct-to-Single-Address or Vault Retention
Forwards full msg.value directly to wallet1 or retains funds inside address(this).
// Called via sc_input_data by Wert Proxy
function myPayableFunctionB(
address recipient, // End-user receiving the NFT/asset
address wallet1, // Destination wallet (not necessary if you use address(this) to keep funds in the wallet)
uint256 itemId
) external payable {
// ... other logic
// Note: msg.value is automatically forwarded by Wert Proxy and will equal commodity_amount from signSmartContractData.
// Option B1: Forward full msg.value to external wallet1
if (wallet1 != address(this)) {
(bool success, ) = payable(wallet1).call{value: msg.value}("");
require(success, "Transfer to wallet1 failed");
}
// Option B2: If wallet1 is address(this), do nothing.
// Native funds automatically remain in contract balance.
_mintItem(recipient, itemId);
// ... other logic
}Path 2: Smart Contracts Accepting ERC-20 Tokens (USDC, USDT, etc.)
ERC-20 tokens require explicit approvals. The Wert Proxy grants an allowance to your smart contract, allowing your contract to pull tokens during execution.
Smart Contract Requirements
- Must Include
amountParameter in Function Signature: Your target function must explicitly accept anamountparameter in its parameters (encoded insidesc_input_data) so it knows how much to pull. - Allowance Limit Requirement: The amount parameter inside
sc_input_datacannot exceedcommodity_amount. The Proxy only approves commodity_amount tokens; attempting to pull more via safeTransferFrom will cause the transaction to revert. - Pull Tokens via
transferFrom: CallsafeTransferFromusingmsg.senderas the source (wheremsg.senderis the Wert Proxy). - No Native
payableNeeded: ERC-20 transfers do not use native transaction value. - Explicit Recipient/Destination Parameters:
msg.senderinside your contract will be the Wert Proxy. Pass user or merchant destination addresses explicitly insidesc_input_data.
Contract Implementation Pattern Examples
Option A: Pulling Tokens Directly to an External Wallet (Direct Transfer)
Pulls approved tokens directly from the Wert Proxy (msg.sender) to wallet1 without holding them in the contract.
// Called via sc_input_data by Wert Proxy
function myERC20FunctionA(
address recipient, // Target user receiving the NFT/asset
address wallet1, // Destination wallet receiving the ERC-20 tokens
uint256 amount, // Token amount (must be <= commodity_amount from signSmartContractData)
uint256 itemId
) external {
// ... other logic
// Note: 'amount' must be less than or equal to commodity_amount set in signSmartContractData.
// Pull approved tokens directly from Wert Proxy (msg.sender) to wallet1
IERC20(usdcToken).safeTransferFrom(msg.sender, wallet1, amount);
_mintItem(recipient, itemId);
// ... other logic
}Option B: Pulling Tokens into Contract Balance (Vault Retention)
Pulls approved tokens from the Wert Proxy (msg.sender) into your smart contract (address(this))
// Called via sc_input_data by Wert Proxy
function myERC20FunctionB(
address recipient, // Target user receiving the NFT/asset
uint256 amount, // Token amount (must be <= commodity_amount from signSmartContractData)
uint256 itemId
) external {
// ... other logic
// Note: 'amount' must be less than or equal to commodity_amount set in signSmartContractData.
// Pull approved tokens from Wert Proxy (msg.sender) into this contract's balance
IERC20(usdcToken).safeTransferFrom(msg.sender, address(this), amount);
_mintItem(recipient, itemId);
// ... other logic
}Option C: Pulling Tokens into Contract and Splitting Between Wallets
Pulls the approved tokens into the contract first, calculates a fee/split, and routes the corresponding amounts to wallet1 and wallet2.
// Called via sc_input_data by Wert Proxy
function myERC20FunctionC(
address recipient, // Target user receiving the NFT/asset
address wallet1, // Primary recipient (e.g., merchant/treasury)
address wallet2, // Secondary recipient (e.g., fee collector)
uint256 amount, // Token amount (must be <= commodity_amount from signSmartContractData)
uint256 wallet2FeeBps, // Fee in basis points (e.g., 250 = 2.5%)
uint256 itemId
) external {
// ... other logic
// Note: Total 'amount' pulled must be less than or equal to commodity_amount set in signSmartContractData.
// 1. Pull total approved tokens from Wert Proxy (msg.sender) into this contract
IERC20(usdcToken).safeTransferFrom(msg.sender, address(this), amount);
// 2. Calculate fee split
uint256 wallet2Amount = (amount * wallet2FeeBps) / 10000;
uint256 wallet1Amount = amount - wallet2Amount;
// 3. Route tokens internally to destinations
if (wallet2Amount > 0) {
IERC20(usdcToken).safeTransfer(wallet2, wallet2Amount);
}
IERC20(usdcToken).safeTransfer(wallet1, wallet1Amount);
_mintItem(recipient, itemId);
// ... other logic
}sOn-Chain Data Minimization & Privacy
Privacy & ComplianceBlockchain data is permanent and publicly accessible. Storing Personally Identifiable Information (PII) such as customer full names, email addresses, or account credentials - directly on-chain in the event logs creates severe privacy and compliance risks.
Keep PII Off-Chain: Retain customer names, emails, and internal account mappings in your secure backend systems.
Use Identifiers On-Chain: Limit data inside
sc_input_dataand event logs strictly to non-sensitive structural references (e.g., target wallet addresses or allocation references). Keep full order details in your backend system, or store only a hashed reference on-chain that can be resolved internally if needed.
If there is a functional or business reason why this detailed metadata must be included on-chain, please let us know, we’re happy to review the constraints and find a balanced solution.
Updated about 10 hours ago

