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: address field from signSmartContractData function

User'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 to sc_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 in sc_input_data

Notice 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

  • payable Modifier Required: Your target function must be marked payable to accept native funds.
  • Read msg.value: The native funds sent by Wert arrive automatically in msg.value.
  • Amount Parameter is Optional: Since native coins arrive via msg.value, an explicit amount parameter inside sc_input_data is typically not needed, though you can include one if your specific logic requires it.
  • Explicit Recipient/Destination Parameters: msg.sender inside your contract will be the Wert Proxy. Pass user or merchant destination addresses explicitly inside sc_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 amount Parameter in Function Signature: Your target function must explicitly accept an amount parameter in its parameters (encoded inside sc_input_data) so it knows how much to pull.
  • Allowance Limit Requirement: The amount parameter inside sc_input_data cannot exceed commodity_amount. The Proxy only approves commodity_amount tokens; attempting to pull more via safeTransferFrom will cause the transaction to revert.
  • Pull Tokens via transferFrom: Call safeTransferFrom using msg.sender as the source (where msg.sender is the Wert Proxy).
  • No Native payable Needed: ERC-20 transfers do not use native transaction value.
  • Explicit Recipient/Destination Parameters: msg.sender inside your contract will be the Wert Proxy. Pass user or merchant destination addresses explicitly inside sc_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
}s

On-Chain Data Minimization & Privacy

🔐

Privacy & Compliance

Blockchain 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_data and 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.



Did this page help you?