ERC-7845 - Universal Orchestrator RPC

Created 2024-11-07
Status Draft
Category ERC
Type Standards Track
Authors

Abstract

"Hey smart speaker, swap my Shiba Inu for Pillar"

"Assistant, how much USDC can I buy with what's in my wallet?"

"Send 10 OP to Vitalik and 5 PEPE to Deimantas"

The Universal Orchestrator RPC aims to standardise the minimum shape and requirements of a request for a solution from an arbitrary system managing an Ethereum wallet to, ultimately, an Orchestrator.

An arbitrary system could be a website, device, app, server program etc - anything that manages an Ethereum wallet, speaks Ethereum JSON-RPC and is looking to request solutions from an Orchestrator.

All solutions from an Orchestrator are ChA¹ (Chain Abstraction-first) by default.

Flow

Motivation

Data model standards can be written in any shape. A system will often expose their external interface but require that the request to the aforementioned interface is modelled in a way that the service understands. This creates a huge level of inconsistency and in turn makes Orchestrator interoperability more difficult.

Orchestrators will become more widespread and numerous over time. This is especially true with the advent of Artificial Intelligence (AI) driven systems, the continued advancement of Human Computer Interaction (HCI) devices (especially those that are voice controlled) and the emergence of Extended Reality (XR) platforms.

Standardising the request object that an Orchestrator can understand from a wallet will drive adoption and make decentralised app development easier for developers that don't know how to make on-chain transactions or have the required technical understanding of block building systems.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

The Orchestrator's external interface(s) that expose functionality to an end-user application or another system MUST use JavaScript Object Notation (JSON).

The following data definitions are available and MUST prefer chain abstraction (ChA¹), unless stated. Chain abstraction means that, where possible, should an asset span multiple chains - expect a solution from an Orchestrator to use and send assets to and from the supported chains to deliver a complete and cost-effective solution.

The following sequence diagram shows the flow of events in this proposal:

Sequence Diagram

This specification follows the top level data shape of Ethereum JSON-RPC requests, as shown below:

Request

The request definition is what a wallet sends to an Orchestrator for solutions to one or more problems. The request follows the specification from Ethereum JSON-RPC.

RPC

interface Rpc: {
    id: number; // REQUIRED
    jsonrpc: string; // REQUIRED
    method: string; // REQUIRED
    params: Problem[]; // REQUIRED
}

The top level definition is an Ethereum RPC object.

Problem

interface Problem: {
    actions: Action[] // REQUIRED
    chainId: number; // OPTIONAL
}

The Problem definition has just one REQUIRED property, actions. The Problem interface leaves space for additional properties in future network upgrades and existing or emerging standards.

Action

interface Action: {
    from: string; // REQUIRED
    towards: (Asset|Destination)[]; // REQUIRED
    with: Offering[]; // OPTIONAL
    type: string; // OPTIONAL
    functionCallName: string; // OPTIONAL
    functionCallData: string; // OPTIONAL
    deadline: number; // OPTIONAL
}

The Action definition has several properties that indicate the desired action. The set properties determine the action that needs to be solved.

Asset

interface Asset: {
    symbol: string; // OPTIONAL
    address: string; // OPTIONAL
    chainId: number; // OPTIONAL
}

The Asset definition defines an asset in question. This definition prefers chain abstraction.

Destination

interface Destination: {
    address: string; // REQUIRED
    chainId: number; // REQUIRED
}

The Destination definition defines a direct target and is used in scenarios where the Orchestrator interpretation MUST NOT be used.

Offering

interface Offering: {
    symbol: string; // SHOULD
    address: string; // SHOULD
    amount: (number|string); // OPTIONAL
    chainId: number; // OPTIONAL
}

The Offering definition defines what the requester is willing to spend from their wallet in order to facilitate the action being solved.

Response

The response definition is what an Orchestrator sends back as a response to the request for solutions from a wallet. The response, like the request, follows the specification from Ethereum JSON-RPC.

RPC

interface Rpc: {
    id: string; // REQUIRED
    jsonrpc: string; // REQUIRED
    result: Solution[]; // REQUIRED
}

The top level object is an Ethereum RPC object.

Solution

interface Solution: {
    name: string; // REQUIRED
    description: string; // OPTIONAL
    transactions: Transaction[]; // REQUIRED
    deadline: number; // OPTIONAL
}

The above Solution interface is the solution to a problem requested above. The Solution MUST be in the same order to a Problem that was requested.

Transaction

interface Transaction: {
    to: Destination; // REQUIRED
    chainId: number; // REQUIRED
    amount: (number|string); // REQUIRED
    calldata: string; // OPTIONAL
}

The above Transaction interface is a transaction definition that allows a wallet to perform their solution to a problem. There may be 1 or more transactions for a Solution.

Rationale

Backwards Compatibility

No backward compatibility issues found.

Reference Implementation

Example requests and responses for solutions

The following examples show a few common scenarios with their requests to, and from, an Orchestrator. All examples are chain abstracted (ChA¹) by default, unless specified.

Sending an ERC-20 token to another address

The following request performs an action:

[!NOTE] Notes: All requests for solutions should be chain abstracted (ChA¹) by default. The Orchestrator > > can check for 5 USDC on any chain for the above "from" address, and send a solution that receives > the 5 USDC on any other chain.

Request to Orchestrator
{
  "id": 1234,
  "jsonrpc": "2.0",
  "method": "orchestrator_findSolutions",
  "params": {
    "problems": [
      {
        "actions": [
          {
            "from": "0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165",
            "towards": {
              "address": "0x50840CE036eEf2005d3c4d6f6Eb65f8116a01629"
            },
            "with": [
              {
                "symbol": "USDC",
                "amount": 5
              }
            ]
          }
        ]
      }
    ]
  }
}
Response from Orchestrator
{
  "id": 1234,
  "jsonrpc": "2.0",
  "result": [
    {
      "name": "Send 5 USDC",
      "description": "Send 5 USDC from 0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165 to 0x50840CE036eEf2005d3c4d6f6Eb65f8116a01629",
      "transactions": [
        {
          "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
          "chainId": 1,
          "calldata": "0x0...",
          "value": 0
        }
      ]
    }
  ]
}

Swapping native token to USDC

The following request performs an action:

[!NOTE] Notes: All requests for solutions should be chain abstracted (ChA¹) by default. The Orchestrator can take 0.1 native asset from any chain in return for USDC on any chain.

Request to Orchestrator
{
  "id": 1337,
  "jsonrpc": "2.0",
  "method": "orchestrator_findSolutions",
  "params": {
    "problems": [
      {
        "actions": [
          {
            "from": "0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165",
            "towards": {
              "symbol": "USDC"
            },
            "with": [
              {
                "amount": 0.1
              }
            ]
          }
        ]
      }
    ]
  }
}
Response from Orchestrator
{
  "id": 1337,
  "jsonrpc": "2.0",
  "result": [
    {
      "name": "Swap 0.1 ETH for 371.498 USDC",
      "description": "Swapping 0.1 ETH from 0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165 to 371.498 USDC via Uniswap",
      "transactions": [
        {
          "to": "0x...",
          "chainId": 1,
          "calldata": "0x0...",
          "value": 0
        },
        {
          "to": "0x...",
          "chainId": 1,
          "calldata": "0x...",
          "value": 0.1
        }
      ]
    }
  ]
}

Swapping multiple tokens to USDC

The following request performs an action:

[!NOTE] Notes: All requests for solutions should be chain abstracted (ChA¹) by default. The Orchestrator can take any amount of SHIB and / or Pillar from any chain in return for an exchanged amount of USDC on any chain.

Request to Orchestrator
{
  "id": 1234,
  "jsonrpc": "2.0",
  "method": "orchestrator_findSolutions",
  "params": {
    "problems": [
      {
        "actions": [
          {
            "from": "0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165",
            "towards": {
              "symbol": "USDC"
            },
            "with": [
              {
                "symbol": "SHIB"
              },
              {
                "symbol": "Pillar"
              }
            ]
          }
        ]
      }
    ]
  }
}
Response from Orchestrator
{
  "id": 1337,
  "jsonrpc": "2.0",
  "result": [
    {
      "name": "Swap 171,246 SHIB and 1004.72 Pillar for 10 USDC",
      "description": "Swapping 171,246 SHIB and 1004.72 Pillar from 0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165 to 10 USDC via Uniswap",
      "transactions": [
        {
          "to": "0x...",
          "chainId": 1,
          "calldata": "0x0...",
          "value": 0
        },
        {
          "to": "0x...",
          "chainId": 1,
          "calldata": "0x0...",
          "value": 0
        },
        {
          "to": "0x...",
          "chainId": 1,
          "calldata": "0x...",
          "value": 0.1
        }
      ]
    }
  ]
}

Sending an ERC-20 token to multiple addresses

The following request performs the following actions:

[!NOTE] Notes: All requests for solutions should be chain abstracted (ChA¹) by default. The Orchestrator can move the specified asset amounts on any chain where the asset exists in the "from" address.

Request to Orchestrator
{
  "id": 1000,
  "jsonrpc": "2.0",
  "method": "orchestrator_findSolutions",
  "params": {
    "problems": [
      {
        "actions": [
          {
            "from": "0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165",
            "towards": {
              "address": "0x50840CE036eEf2005d3c4d6f6Eb65f8116a01629"
            },
            "with": [
              {
                "symbol": "USDC",
                "amount": 5
              }
            ]
          },
          {
            "from": "0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165",
            "towards": {
              "address": "0xFCd239451346238B5560511Ae47A0b82b1bbE9f0"
            },
            "with": [
              {
                "symbol": "PLR",
                "amount": 100
              }
            ]
          }
        ]
      }
    ]
  }
}
Response from Orchestrator
{
  "id": 1000,
  "jsonrpc": "2.0",
  "result": [
    {
      "name": "Send 5 USDC to 0x...629 and 100 PLR to 0x...9f0",
      "description": "Send 5 USDC to 0x50840CE036eEf2005d3c4d6f6Eb65f8116a01629 and 100 PLR to 0xFCd239451346238B5560511Ae47A0b82b1bbE9f0",
      "transactions": [
        {
          "to": "0x...",
          "chainId": 1,
          "calldata": "0x0...",
          "value": 0
        },
        {
          "to": "0x...",
          "chainId": 1,
          "calldata": "0x...",
          "value": 0
        }
      ]
    }
  ]
}

Calling a Smart Contract function on Polygon: Inscribing a message which costs 1 USDC

The following request performs an action:

[!NOTE] Notes: All requests for solutions should be chain abstracted (ChA¹) by default - HOWEVER in this example, the chainId property on the Destination interface has been specified. The operation should now be locked to the specified chain. Because functionCallName and functionCallData exist, the Orchestrator can infer that this is a smart contract call and act accordingly.

Request to Orchestrator
{
  "id": 420,
  "jsonrpc": "2.0",
  "method": "orchestrator_findSolutions",
  "params": {
    "problems": [
      {
        "actions": [
          {
            "from": "0xbafB4E1EFA94B359e2E175CF6156AedA2cACa165",
            "towards": {
              "address": "0x50840CE036eEf2005d3c4d6f6Eb65f8116a01629",
              "chainId": 137
            },
            "with": [
              {
                "symbol": "USDC",
                "amount": 1
              }
            ],
            "functionCallName": "inscribe",
            "functionCallData": "0x..."
          }
        ]
      }
    ]
  }
}
Response from Orchestrator
{
  "id": 420,
  "jsonrpc": "2.0",
  "result": [
    {
      "name": "Inscribe with 1 USDC",
      "description": "Call the Inscribe function with 1 USDC on Polygon",
      "transactions": [
        {
          "to": "0x...",
          "chainId": 137,
          "calldata": "0x0...",
          "value": 0
        },
        {
          "to": "0x...",
          "chainId": 137,
          "calldata": "0x...",
          "value": 0
        }
      ]
    }
  ]
}

Security Considerations

Orchestrator reputation

The ability for anyone to build an Orchestrator inherently brings the opportunity for code errors and therefore a degraded service. Orchestrators may also be abandoned over time. A reputation score should be leveraged by the Orchestrator to determine if the Orchestrator is fit for purpose. This should be up to the requesting system or wallet to determine.

Orchestrator producing dishonest solutions

An Orchestrator may return transactions as part of a solution that are wrong or attempt to take more than what was asked of it. Where possible, the Orchestrator should validate returned transaction address destinations and any other data.

Orchestrator personality variations

Whilst not a security consideration per-se, some Orchestrators may gravitate towards their own business targets which may skew the outcome of Orchestrator solutions. The systems or wallets requesting solutions from Orchestrators should be mindful of this unless it is intended.

Copyright

Copyright and related rights waived via CC0.