Signing transfer requests

Each transfer request must have its transfer details signed by a dedicated wallet. This should be the same wallet address that was configured as the signer when the contract was first deployed.

This is to ensure that none of the transfer details are changed at any point between the time they are sent to Loop and the time they are executed in the contract.

Transfer request are expected to be signed using the standard defined in EIP-712. See Securing with signatures for more information.

Parameters to sign

The 6 transfer parameters that need to be signed are:

ParameterDescription

invoiceId

The ID of the invoice

from

The from address

to

The to address

token

The payment token address

amount

The amount to be billed

usd

Whether the transfer amount is denominated in USD

These transfer parameters are sent to the smart contract along with the signature which are then validated on-chain against the signer address stored in the contract. If the on-chain validation of the parameters does not match the stored signer address during validation, the transfer for that order will fail and not be processed.

Example of how to sign transfer orders

const domain = {
    name: "LoopVariableRatesContract",
    version: "1",
    chainId: 137,
    verifyingContract: "0x26f1cB8611b15E6e3859d8Cf0ef294b7855d2135"
};

const types = {
    Transfer: [
        { name: "invoiceId", type: "string" },
        { name: "from", type: "address" },
        { name: "to", type: "address" },
        { name: "token", type: "address" },
        { name: "amount", type: "uint256" },
        { name: "usd", type: "bool" }
    ]
};

const transfer1 = {
    invoiceId: "ch_3LjpW92eZvKYlo2C0RYz8S7X", // e.g. a Stripe charge id
    from: "0xad4efce746f129a9df375af2ddcf9097531eb466",
    to: "0x0f2672BA12aed17BEe075F7AEabC24b98E3098Ca",
    token: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
    amount: 100000000,  // e.g. 100 USDC
    usd: false
};
// Sign first transfer
const signature1 = await signer._signTypedData(
    domain,
    types,
    transfer1
);
// Construct first transfer order object with signature, entity ID assumed from API key
const transferOrder1 = {
    ...transfer1,
    signature1,
    itemId: "2a2f4ff3-6e26-4da3-a569-25f52492b112",
    billDate: "1664096943",
    networkId: 137
}

const transfer2 = {
    invoiceId: "ch_1LlilL2eZvKYlo2ClNgXnUbd", // e.g. a Stripe charge id
    from: "0x02F84A56e4ebBA0F7840aab2664Ad1C8476B5Ed5",
    to: "0x0f2672BA12aed17BEe075F7AEabC24b98E3098Ca",
    token: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
    amount: "10000000000",  // e.g. 100 USD
    usd: true
};

// Sign second transfer
const signature2 = await signer._signTypedData(
    domain,
    types,
    transfer2
);

// Construct second transfer order object with signature
const transferOrder2 = {
    ...transfer2,
    signature2,
    itemId: "8695edec-c310-4ddc-b348-01087b2d730e",
    billDate: "1664272491",
    networkId: 137,
    entityId: "3be77c6d-91e8-4507-81e9-b02be71d0f60"
}

// POST transfer orders to Loop API
const res = await axios.post(
    "https://api.loopcrypto.xyz/api/v1/transfers", 
    [
        transferOrder1,
        transferOrder2
    ],
    {
        headers: {
            "api-key": "eyJzaWduYXR1cmUiOiIweGU5Y2YwY2Y2YmM2NmV..."
        }
    }
);

Last updated