Kargzul
Kargzul

Reputation: 56

Chainlink functions: Handling API calls, secret transmission and flow coordination in a blockchain-based P2P token swap system

I'm currently working on a protocol that acts as an intermediary for peer-to-peer token transactions using PayPal for payments. Here is the flow:

The protocol needs to act as a middleman and receive first the Paypal payment, as it can't accept or approve orders for others.

However, I'm facing some challenges:

How do I handle the call to Chainlink functions amidst all these subsequent actions? Is there an optimal way to structure this flow? Some of the actions, particularly the API requests, require secrets for authentication. What would be the best way to securely send these secrets between contract calls?

Current idea for a solution:

This is the function that should be called after the meta-txn have been processed by the relayer. I put the JS code to be sent to my functionsConsumer and this should make the request?

function capturePayment(
    uint256 order_id,
    uint256 id,
    address makerAddress,
    string memory paypalEmail,
    uint256 amountToBuy,
    string memory currency,
    uint256 nonce,
    bytes memory clientId,
    bytes memory clientSecret
  ) internal {
    string
      memory source = "const authUrl = 'https://api-m.sandbox.paypal.com/v1/oauth2/token'; const base64 = Buffer.from(`${secrets.clientId}:${secrets.clientSecret}`).toString('base64'); const order_id = args[0]; const body_data = { grant_type: `client_credentials`, }; let formBody = []; for (let property in body_data) { let encodedKey = encodeURIComponent(property); let encodedValue = encodeURIComponent(body_data[property]); formBody.push(encodedKey + `=` + encodedValue); } formBody = formBody.join(`&`); const responseAuthToken = Functions.makeHttpRequest({ url: authUrl, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json', 'Accept-Language': 'en_US', Authorization: `Basic ${base64}`, }, data: formBody, }); const { data } = await responseAuthToken; const { access_token } = data; const response = Functions.makeHttpRequest({ url: `https://api-m.sandbox.paypal.com/v2/checkout/orders/${order_id}/capture`, method: 'POST', headers: { 'Content-Type': 'application/json', 'PayPal-Request-Id': '7b92603e-77ed-4896-8f78-5dea2050476a', Authorization: `Bearer ${access_token}`, }, }); const responseData = await response; console.log(responseData); return Functions.encodeString(JSON.stringify(responseData));";

    bytes32 reqID = functionsConsumer.executeRequest(
      source,
      [clientId, clientSecret],
      [order_id],
      subscriptionId,
      gasLimit
    );
}

Here in the callback of the function I check that I get (for now) a string of length 13 which should be the order_id that has been fulfilled. After this it should call the swap in my relayer(receiver) contract. I am sending the order_id to access a mapping for getting back all the info of that Transaction data.

FunctionsConsumer.sol

function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override {
    latestResponse = response;
    latestError = err;
    if (latestResponse.length == 13) {
      receiver.processTransaction(latestResponse);
    }
    emit OCRResponse(requestId, response, err);
  }

Here we should perform the swap, while in the end calling the last chainlink function to perform the payment: Receiver.sol

function performSwaps(bytes calldata order_id) public onlyConsumer {
    Transaction memory transaction = orderIdToTransaction[order_id];

    contract2.performSwaps(transaction.id, transaction.amountToBuy, transaction.makerAddress);

    sendPayment(
      transaction.clientId,
      transaction.clientSecret,
      transaction.paypalEmail,
      transaction.amountToBuy,
      transaction.currency
    );
  }

The code of sendPayment is the same as capturePayment just the JS changes.

Any help or guidance on how to implement this would be greatly appreciated. Thank you!

Upvotes: 0

Views: 187

Answers (1)

Richard G
Richard G

Reputation: 308

it looks like a lot is going on in this question. Hopefully, this gets your questions answered.

How do I handle the call to Chainlink functions amidst all these subsequent actions?

You will need something to call the smart contract directly to invoke functions. You could do this from the signing interface. When the user signs, it could also call the smart contract.

What would be the best way to securely send these secrets between contract calls?

Passing secrets to the API is handled via secrets.YOUR_KEY_HERE you can learn more about it in the Functions Docs

I put the JS code to be sent to my functionsConsumer and this should make the request?

Yes, this will allow the code to be passed to the DON

Side Note

In the callback function, you mention checking the length of the string. Please ensure that you have decoded the value before checking the length.

Upvotes: 0

Related Questions