Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Native transfers #1157

Closed
typedarray opened this issue Oct 11, 2024 · 7 comments · Fixed by #1210 or #1235
Closed

[Feature] Native transfers #1157

typedarray opened this issue Oct 11, 2024 · 7 comments · Fixed by #1210 or #1235
Labels
A: Indexing engine Area: Indexing engine A: Sync engine Area: Sync engine T: Feature Type: Feature

Comments

@typedarray
Copy link
Collaborator

We should add an intuitive high-level API for native transfers, such that you can write indexing functions that fire when a specific account (or list of accounts specified by a factory) sends or receives any amount of the native token.

@khaidarkairbek
Copy link
Collaborator

If you could clarify on the native transfers. As I understand, they are not shown in the logs, but in call traces, right? Would it be an additional filter on the call traces then?

@kyscott18 kyscott18 added T: Feature Type: Feature A: Sync engine Area: Sync engine A: Indexing engine Area: Indexing engine labels Oct 15, 2024
@khaidarkairbek
Copy link
Collaborator

I am suggesting the API similar to contracts in the createConfig:

export default createConfig({
  networks: {
    mainnet: { chainId: 1, transport: http(process.env.PONDER_RPC_URL_1) },
  },
  contracts: {
    SudoswapPool: {
      abi: SudoswapPoolAbi,
      network: "mainnet",
      factory: {
        // The address of the factory contract that creates instances of this child contract.
        address: "0xb16c1342E617A5B6E4b631EB114483FDB289c0A4",
        // The event emitted by the factory that announces a new instance of this child contract.
        event: parseAbiItem("event NewPair(address poolAddress)"),
        // The name of the parameter that contains the address of the new child contract.
        parameter: "poolAddress",
      },
      startBlock: 14645816,
    },
  },
  accounts: {
    Alice: {
      network: "mainnet", 
      address: [
        "0x...", 
        "0x...", 
        "0x..."
      ], 
      startBlock: 14645816
    }
  }
});

Similarly, if we would like to track accounts generated from the factory:

export default createConfig({
  networks: {
    mainnet: { chainId: 1, transport: http(process.env.PONDER_RPC_URL_1) },
  },
  contracts: {
    SudoswapPool: {
      abi: SudoswapPoolAbi,
      network: "mainnet",
      address: "0xb16c1342E617A5B6E4b631EB114483FDB289c0A4"
      startBlock: 14645816,
    },
  },
  accounts: {
    SudoswapUsers: {
      network: "mainnet", 
      factory: {
        // The address of the factory contract that creates instances of this child contract.
        address: "0xb16c1342E617A5B6E4b631EB114483FDB289c0A4",
        // The event emitted by the factory that announces a new instance of this child contract.
        event: parseAbiItem("event Deposit(address sender, uint256 amount)"),
        // The name of the parameter that contains the address of the new child contract.
        parameter: "sender",
      }, 
      startBlock: 14645816
    }
  }
});

This way our accounts config api would be close to what we have for contracts already.

@typedarray
Copy link
Collaborator Author

This is a great start. Some follow-up questions to consider:

  • What would the indexing function keys/"event names" look like for accounts? For contracts, we use the pattern ponder.on("ContractName:EventName", ...) and ponder.on("ContractName.functionName", ...). And, what would be the type of the event object within the indexing function?
  • This config API seems sufficient for native token transfers. Now, what if the user also wants to index all ERC20 transfers to and from an account? At a low level, this would look like: index all ERC20 transfers where the to (or from) argument equals the account's address, or the following eth_getLogs filter:
{
  address: null, // Include all ERC20 contracts
  topics:[
    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Transfer selector
    null, // Include all senders
    "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078" // Target account address as recipient
  ]
}
  • (cont) To support this feature, we'll need to come up with an intuitive config API for specifying the event ABI, and which indexed parameter should "match" the account's address. This API would be particularly powerful if it's extensible to any event, not just the standards (ERC20/ERC721).

Here's a concrete user scenario that will use this feature: A smart account infrastructure provider wants to build an API that serves a list of tokens (native, ERC20, ERC721) for each account created using their tool. The smart accounts are contracts (not EOAs) that get created via a factory contract that is compatible with our existing factory pattern.

@khaidarkairbek
Copy link
Collaborator

khaidarkairbek commented Oct 18, 2024

The first idea that comes to mind:

export default createConfig({
  networks: {
    mainnet: { chainId: 1, transport: http(process.env.PONDER_RPC_URL_1) },
  },
  accounts: {
    Alice: {
      network: "mainnet", 
      address: [                        // only native transfers (if no events are given)
        "0x...", 
        "0x...", 
        "0x..."
      ], 
      startBlock: 14645816
    }, 
    Bob: {
      network: "mainnet", 
      address: [                        // no native transfers, but event logs
        "0x...", 
        "0x...", 
        "0x..."
      ], 
      events: [                        //list of events to index with corresponding event + parameter(s)
        {
          abi: ERC20Abi,                  //first version, uses an ABI and specifies only eventName
          eventName: 'TransferFrom', 
          parameter: ["from", "to"]     
        }, 
        {
          event: parseAbiItem(
            "event TransferFrom(address indexed from, address indexed to, uint256 value)"
          ),                              //second version, uses parseAbiItem similar to factory config
          parameter: "from"
        },
        ...
      ],
      startBlock: 14645816, 
      includeNativeTransfers: false               // true or false, by default? 
    }, 
  }
});

Then, the indexing functions would look as follows:

//The indexing function for events: 
ponder.on("AccountName:EventName", async ({event, context}) => {
  
})

//The indexing function for native transfers:
ponder.on("AccountName", async ({event, context}) => {
  
})

The types for both events I would assume can be identical to those used for indexing contract events and callTraces.

@kyscott18
Copy link
Collaborator

Somewhat unrelated to the comments above, I think another feature we want with accounts is the ability to index all transactions to and from an account.

A concrete example of when this would be useful: tracking the sequencer address on the base chain for a layer 2 rollup.

@khaidarkairbek
Copy link
Collaborator

Good point, we would be also interested in filtering native transfers based on "from", "to" parameters. The revised version of the api design would be as shown below.

export default createConfig({
  networks: {
    mainnet: { chainId: 1, transport: http(process.env.PONDER_RPC_URL_1) },
  },
  accounts: { 
    Bob: {                                     //the user needs to specify events, nativeTransfers or transactions
      network: "mainnet", 
      address: [                        
        "0x...", 
        "0x...", 
        "0x..."
      ], 
      events: [                        //list of events to index with corresponding event + parameter(s)
        {
          abi: ERC20Abi,                  //first version, uses an ABI and specifies only eventName
          eventName: 'TransferFrom', 
          parameter: ["from", "to"]     
        }, 
        {
          event: parseAbiItem(
            "event TransferFrom(address indexed from, address indexed to, uint256 value)"
          ),                              //second version, uses parseAbiItem similar to factory config
          parameter: "from"
        },
        ...
      ],
      nativeTransfers: "from" | "to" | ["from", "to"],
      transactions: "from" | "to" | ["from", "to"],
      startBlock: 14645816
    }, 
  }
});

For transactions, the indexing function might look as follows:

ponder.on("AccountName:transaction", async ({event, context}) => {
  
})

And type of the event for transaction would be:

{
  block: Block,
  transaction: Transaction, 
  transactionReceipt: TransactionReceipt | undefined
}

@kyscott18
Copy link
Collaborator

We decided that were gonna focus on the internal filter types and work towards offering a low-level api that allows users to directly define a filter. Later on, we can build a high-level "account" api that abstracts away the filters.

Notes on filter types

Filters should be deliberately designed, expressive, and modeled after the fundamentals of the Ethereum blockchain, not necessarily the Ethereum standard JSON-RPC. 


type TransferFilter = {type: "transfer";chainId: number;
  fromAddress: Address | Address[] | Factory | undefined;
  toAddress: Address | Address[] | Factory | undefined;
  fromBlock: number;
  toBlock: number | undefined;includeTransactionReceipt: boolean;
}

type TransactionFilter = {
  type: "transaction";
  chainId: number;
  fromAddress: Address | Address[] | Factory | undefined;
  toAddress: Address | Address[] | Factory | undefined;
  fromBlock: number;
  toBlock: number | undefined;includeTransactionReceipt: boolean;
}

I realized TransactionFilter is very similar to CallTraceFilter, except that it only contains top-level calls. I think it's still fine to separate them, but worth keeping in mind.

@kyscott18 kyscott18 linked a pull request Nov 5, 2024 that will close this issue
28 tasks
@kyscott18 kyscott18 mentioned this issue Dec 5, 2024
@kyscott18 kyscott18 linked a pull request Dec 5, 2024 that will close this issue
@github-project-automation github-project-automation bot moved this from Todo to Done in Ponder Roadmap Dec 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A: Indexing engine Area: Indexing engine A: Sync engine Area: Sync engine T: Feature Type: Feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants