🥜
Peanut Docs
  • Learn
    • 🥜What is Peanut?
    • 🏦Cashout
      • Supported geographies
    • 📩How to use Peanut Links?
      • ⚙️How do Peanut Links Work?
      • 🔒Trust assumptions
    • 📌Use cases
    • 📚Case Studies
      • 🎁Welcome Packs
      • 📘Raffles to Boost UWAs and Transactions
      • 📗Sending Testnet Tokens at Hackathons
      • 📙IRL Events Marketing
    • ⛓️Supported Chains and Tokens
    • 💰Pricing
    • 🆘Support
  • Integrate
    • Using the SDK
      • Create ClaimLinks
        • Create Link
        • Batch Create Links
        • Create Raffle Links
        • Create Multi-Token Link
        • Create NFT Link
        • Create Gasless Links
        • Create Branded Links
      • Claim
        • Claim Link
        • Claim Gasless Link
        • Claim Cross-Chain Link
        • Claim Raffle Link
        • Claim Link as Sender (Reclaiming)
      • Create Request Link
      • Pay a Request Link
      • Pay a Request Link X-Chain
      • Utils
        • Get Link Details
        • Cross-chain
          • Get Supported Destination Chains
          • Get Supported Destination Tokens
          • Get Cross-Chain Options
          • Get Cross-Chain Route
        • Raffle (Legacy)
          • Get Raffle Info
          • Get User Raffle Status
          • Get Raffle Leaderboard
        • Get Default Provider
        • Get Supported Peanut Chains
        • Toggle Verbosity
        • EthersV5 <> Peanut Transaction Types
        • Estimate Fee Options
        • Get Random String
        • Get all deposits for an Address
        • Get Token Balance
      • API Keys
      • White-Labelling
      • Troubleshooting
      • SDK FAQ
    • Embedding an IFrame
    • Integrate Directly in Smart Contracts
    • Wallet Integrations
      • 🎬UI Examples
  • Other
    • 👾Bug bounties
    • 🔓Security Audit
    • 📜Peanut Protocol Contracts
  • BLOGS
    • Transfer Abstraction
    • Can We Sidestep Onchain Identity?
  • Additional Links
    • 🐦Twitter
    • 😊Telegram
    • 🤙Discord
    • 🥜Work with Us
    • 🎨Press Kit
    • 👨‍⚖️Contact and Legal
Powered by GitBook
On this page
  • Creating a Link
  • Step 1: Generating Keys
  • Step 2: Calling the Smart Contract
  • Step 3: Creating the Link
  • Claiming a Link
  • Withdrawing a Link
  1. Integrate

Integrate Directly in Smart Contracts

PreviousEmbedding an IFrameNextWallet Integrations

Last updated 1 year ago

This is a somewhat antiquated page. Please reach out to us.

It is recommended developers familiarize themselves with prior to attempting direct contract interactions.

The Peanut Smart Contracts are designed for sending non-front-runnable link payments, which can be in the form of various assets. Including ETH, ERC-20 tokens, ERC-721 tokens, ERC-1155 tokens, or combinations thereof!

Contracts use asymmetric ECDSA encryption to verify claim permissions and prevent front running.

Creating a Link

To create a Link there are a few steps required.

  1. Generate the seed and derive asymmetric key pair from ID.

  2. Call the smart contract to deposit funds.

  3. Get index from the event log emitted by deposit call.

Step 1: Generating Keys

Each Peanut Link is protected by a key. The key should be securely created for each Link using secure random data generation features of the platform used.

Whilst anything can be used as a key, the following should be avoided:

  • Short seeds less than 16 characters

  • Words or common patterns (e.g. aaaaaa or 123456)

The SDK uses Ethers to generate the . Any equivalent function in different languages should also be able to do the same.

function generateKeysFromString(string) {
    var privateKey = ethers.utils.keccak256(
			ethers.utils.toUtf8Bytes(string))
    var wallet = new ethers.Wallet(privateKey)
    return {
        address: wallet.address,
        privateKey: privateKey,
    }
}

It's worth checking that the output from any other crypto library matches the output of the generateKeysFromString function within util.js.

The address and private key from this generation should be stored for later use in the next steps.

Step 2: Calling the Smart Contract

The makeDeposit function allows users to create a Link from a deposit. It handles various token types (ETH, ERC-20, ERC-721, ERC-1155, etc) and stores deposit information in the deposits array.

This function should be called with the correct parameters for the type of crypto being sent along with the last 20 bytes of the public key generated in the previous step.

The following table lists all parameter types for the makeDeposit function.

Param
Description

_tokenAddress

Address of the token being stored in the Peanut link

_contractType

Enumeration of type of crypto being wrapped in a link.

0 = ETH / native chain token

1 = ERC20 token

2 = ERC721 token

3 = ERC1155 token

4 = ECO token

_amount

Amount of token being sent

_tokenId

ID for ERC721 and ERC1155 type contracts (leave as 0 for any type apart from 2 or 3)

_pubKey20

Last 20 bytes of the public key for ECDSA signing

function makeDeposit(
    address _tokenAddress,
    uint8 _contractType,
    uint256 _amount,
    uint256 _tokenId,
    address _pubKey20
) public payable nonReentrant returns (uint256) {
    // Function for making a deposit.
    // ...
}

Step 3: Creating the Link

Once the transaction calls makeDeposit successfully, it will emit a log confirming the deposit and index slot where the funds are held.

event DepositEvent(
    uint256 indexed _index,
    uint8 indexed _contractType,
    uint256 _amount,
    address indexed _senderAddress
);

From the event log, we can see the deposit was made to index 0 for an ERC20 token.

All the information supplied in the event log will help us construct the Peanut Link. Remember the expected format:

https://peanut.to/claim?c=CHAIN&i=INDEX&v=VERSION&p=KEY

With the information from the previous steps, we can append the information and send this link to a user.

Key
Value

c

ChainId expressed numerically for chain contract was called on. For example, in the contract call referenced above, this was made on Optimism mainnet. Therefore the c value would be 10.

i

Index slot of deposit. From the event above we know this is 0

v

p

Key generated in step 1 for the ECDSA key pair.

Alternatively, we can simply store at minimum the following information to be able to claim in the future:

  • Chain and Contract Address

  • Deposit Index

  • Key used to generate ECDSA Key Pair

Claiming a Link

When claiming a Link, the following information needs to be provided to the contract:

  • Index slot of the deposit

  • Destination address to send funds

  • Hash and signature of destination address signed with the key pair used when depositing

    function withdrawDeposit(
        uint256 _index,
        address _recipientAddress,
        bytes32 _recipientAddressHash,
        bytes memory _signature
    ) external nonReentrant returns (bool) {
        // Function for withdrawing a deposit to a recipient.
        // ...
    }

The hashed recipient address is expected to be encoded according to EIP191 with the appropriate prefix. As an example, the SDK uses the following function to generate the hash of the address.

function solidityHashBytesEIP191(bytes) {
    return ethers.utils.hashMessage(bytes)
}

Then the signature is generated by signing the the hashed address using the private key generated prior when creating the ECDSA key pair from the random seed. For testing, make sure your hash and signature values match the following when using the test address 0xF4CF81931bb32E41CfdB24Ba10492C019A5ec5f9.

recipientAddress:  0xF4CF81931bb32E41CfdB24Ba10492C019A5ec5f9
addressHash:  0xed38f27bd5dfeb28a8fb1b0aecb00b86291e020d69825021c0320ede88434902
addressHashEIP191:  0xaef76579ddfee326849c76236d0aafce11a58f44b00b08cf3d0077c494a31657
signature:  0x8926942ac2254dcbd8d14ded150454a18d77d2e7073a8e4a663128fe33ed4c5d63e0617ccd1f80c8f754933a2cdad255542180c7be7f04eaeca40d9d1ead5d651c

When a Link has been successfully claimed, a WithdrawEvent event will be emitted from the contract call. The WithdrawEvent event will confirm the transfer alongside the funds that are sent to the recipient address.

event WithdrawEvent(
    uint256 indexed _index,
    uint8 indexed _contractType,
    uint256 _amount,
    address indexed _recipientAddress
);

Withdrawing a Link

Any Link can be cancelled after 24 hours of creation by the original address that created it

To cancel and reclaim the funds for a Link that have not been claimed, call the function withdrawDepositSender with the index slot parameter.

   function withdrawDepositSender(uint256 _index)
      external nonReentrant returns (bool) {
        // Function for allowing a sender to withdraw their deposit.
        // ...
    }

Finding the latest address of the contract on the chain in question can be done by looking at the in the github repo. Please note this same smart contract will need to be called in order to claim the funds.

For example, in this the event log 104 contains information about the funds stored and what storage index they occupy.

Version identifying the contract. The version is defined as a string with the format vXXX in the SDK. Reference the to look up the correct value for the contract and chain in question.

The order of these parameters are clearly identified in the contract function signature.

Not to be confused with withdrawing funds from a Link (i.e. ). This process allows the original depositor / creator of the Link to retract the funds and cancel.

how the Peanut Protocol works
ECDSA key from a seed
registry file
deposit
withdrawDeposit
claiming a link
registry file
The photo shows an example of deposit event.