Skip to content

Commit

Permalink
Fix compatibility with new ERC6551 contract
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastienGllmt committed Oct 23, 2023
1 parent 4a3cbd7 commit ba4c322
Show file tree
Hide file tree
Showing 9 changed files with 1,099 additions and 3,620 deletions.
4,325 changes: 726 additions & 3,599 deletions package-lock.json

Large diffs are not rendered by default.

49 changes: 40 additions & 9 deletions packages/engine/paima-funnel/src/cde/erc6551Registry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ChainDataExtensionDatumType, DEFAULT_FUNNEL_TIMEOUT, timeout } from '@paima/utils';
import {
ChainDataExtensionDatumType,
DEFAULT_FUNNEL_TIMEOUT,
ERC6551_REGISTRY_DEFAULT,
timeout,
} from '@paima/utils';
import type {
CdeErc6551RegistryDatum,
ChainDataExtensionDatum,
Expand All @@ -12,11 +17,16 @@ export default async function getCdeData(
toBlock: number
): Promise<ChainDataExtensionDatum[]> {
const { implementation, tokenContract, tokenId } = extension;
const filter = {
...(implementation !== null && implementation !== undefined ? { implementation } : {}),
...(tokenContract !== null && tokenContract !== undefined ? { tokenContract } : {}),
...(tokenId !== null && tokenId !== undefined ? { tokenId } : {}),
};

// old ERC6551 did not have any indexed fields
const filter =
extension.contractAddress === ERC6551_REGISTRY_DEFAULT.Old
? {}
: {
...(implementation != null ? { implementation } : {}),
...(tokenContract != null ? { tokenContract } : {}),
...(tokenId != null ? { tokenId } : {}),
};
// TOOD: typechain is missing the proper type generation for getPastEvents
// https://github.com/dethcrypto/TypeChain/issues/767
const events = (await timeout(
Expand All @@ -29,9 +39,30 @@ export default async function getCdeData(
)) as unknown as AccountCreated[];

// salt is not an indexed field, so we have to run the filter after the fact
const filteredEvents =
extension.salt == null ? events : events.filter(e => e.returnValues.salt === extension.salt);
return filteredEvents.map((e: AccountCreated) => toDatum(e, extension)).flat();
const filteredEvents = ((): AccountCreated[] => {
let withFilter =
extension.salt == null ? events : events.filter(e => e.returnValues.salt === extension.salt);

// new ERC6551 uses indexed fields, so we can just return early
if (extension.contractAddress !== ERC6551_REGISTRY_DEFAULT.Old) {
return withFilter;
}
withFilter =
implementation == null
? withFilter
: withFilter.filter(e => e.returnValues.implementation === extension.implementation);
withFilter =
tokenContract == null
? withFilter
: withFilter.filter(e => e.returnValues.tokenContract === extension.tokenContract);
withFilter =
tokenId == null
? withFilter
: withFilter.filter(e => e.returnValues.tokenId === extension.tokenId);
return withFilter;
})();
const result = filteredEvents.map((e: AccountCreated) => toDatum(e, extension)).flat();
return result;
}

function toDatum(
Expand Down
5 changes: 5 additions & 0 deletions packages/engine/paima-funnel/src/reading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export async function getBaseChainDataSingle(

async function getBlockData(web3: Web3, blockNumber: number): Promise<ChainData> {
const block = await timeout(web3.eth.getBlock(blockNumber), DEFAULT_FUNNEL_TIMEOUT);
if (block == null) {
throw new Error(
`Unable to find block number ${blockNumber}. Perhaps it no long exists due to a rollback or load-balancing`
);
}
return blockDataToChainData(block);
}

Expand Down
23 changes: 16 additions & 7 deletions packages/engine/paima-runtime/src/cde-config/loading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import {
getPaimaErc721Contract,
getAbiContract,
getErc6551RegistryContract,
getOldErc6551RegistryContract,
ERC6551_REGISTRY_DEFAULT,
} from '@paima/utils';

import type {
ChainDataExtension,
ChainDataExtensionErc6551Registry,
ChainDataExtensionGeneric,
TChainDataExtensionErc721Config,
TChainDataExtensionGenericConfig,
Expand All @@ -37,9 +40,6 @@ import assertNever from 'assert-never';
import fnv from 'fnv-plus';
import stableStringify from 'json-stable-stringify';

/** Default registry address specified in ERC6551 */
const ERC6551_REGISTRY_DEFAULT = '0x02101dfB77FDE026414827Fdc604ddAF224F0921'.toLowerCase();

type ValidationResult = [config: ChainDataExtension[], validated: boolean];

export async function loadChainDataExtensions(
Expand Down Expand Up @@ -128,7 +128,10 @@ function checkOrError<T extends TSchema>(
}

function hashConfig(config: any): number {
return fnv.fast1a32(stableStringify(config));
// fnv returns an unsigned int, but postgres doesn't support unsigned ints
const unsignedInt = fnv.fast1a32(stableStringify(config));
// map unsigned into signed in. Obviously this isn't lossless, but it's still good enough for collision avoidance
return Math.floor(unsignedInt / 2);
}

// Do type-specific initialization and construct contract objects
Expand Down Expand Up @@ -175,21 +178,27 @@ async function instantiateExtension(
case CdeEntryTypeName.Generic:
return await instantiateCdeGeneric(config, index, web3);
case CdeEntryTypeName.ERC6551Registry:
const contractAddress = config.contractAddress ?? ERC6551_REGISTRY_DEFAULT;
const contractAddress = config.contractAddress ?? ERC6551_REGISTRY_DEFAULT.New;
return {
...config,
cdeId: index,
hash: hashConfig(config),
cdeType: ChainDataExtensionType.ERC6551Registry,
contractAddress,
contract: getErc6551RegistryContract(contractAddress, web3),
contract: ((): ChainDataExtensionErc6551Registry['contract'] => {
if (contractAddress === ERC6551_REGISTRY_DEFAULT.Old) {
return getOldErc6551RegistryContract(contractAddress, web3);
}
// assume everything else is using the new contract
return getErc6551RegistryContract(contractAddress, web3);
})(),
};
default:
assertNever(config);
}
}

async function isPaimaErc721(
export async function isPaimaErc721(
cdeConfig: TChainDataExtensionErc721Config,
web3: Web3
): Promise<boolean> {
Expand Down
6 changes: 4 additions & 2 deletions packages/engine/paima-runtime/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import type { Pool, PoolClient, PoolConfig } from 'pg';
import type { Express, RequestHandler } from 'express';

import type { SQLUpdate } from '@paima/db';
import type { ChainDataExtensionDatumType, ChainDataExtensionType } from '@paima/utils';
import type {
ChainDataExtensionDatumType,
ChainDataExtensionType,
Contract,
ERC20Contract,
ERC721Contract,
VersionString,
SubmittedChainData,
SubmittedData,
PaimaERC721Contract,
OldERC6551RegistryContract,
ERC6551RegistryContract,
} from '@paima/utils';
import { Type } from '@sinclair/typebox';
Expand Down Expand Up @@ -231,7 +233,7 @@ export const ChainDataExtensionErc6551RegistryConfig = Type.Intersect([
export type ChainDataExtensionErc6551Registry = ChainDataExtensionBase &
Static<typeof ChainDataExtensionErc6551RegistryConfig> & {
cdeType: ChainDataExtensionType.ERC6551Registry;
contract: ERC6551RegistryContract;
contract: ERC6551RegistryContract | OldERC6551RegistryContract;
};

export const CdeConfig = Type.Object({
Expand Down
6 changes: 4 additions & 2 deletions packages/paima-sdk/paima-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@
"typechain": "^8.3.1"
},
"dependencies": {
"@metamask/eth-json-rpc-middleware": "^12.0.0",
"@metamask/json-rpc-engine": "^7.2.0",
"@sinclair/typebox": "^0.31.17",
"algosdk": "^2.3.0",
"flatted": "^3.2.7",
"i": "^0.3.7",
"npm": "^10.0.0",
"yaml": "^2.3.1",
"web3": "1.10.0"
"web3": "1.10.0",
"yaml": "^2.3.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
{
"contractName": "IERC6551Registry",
"abi": [
{
"inputs": [],
"name": "InitializationFailed",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "implementation",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "tokenContract",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "salt",
"type": "uint256"
}
],
"name": "AccountCreated",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "implementation",
"type": "address"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
},
{
"internalType": "address",
"name": "tokenContract",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
}
],
"name": "account",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "implementation",
"type": "address"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
},
{
"internalType": "address",
"name": "tokenContract",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "initData",
"type": "bytes"
}
],
"name": "createAccount",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]
}
Loading

0 comments on commit ba4c322

Please sign in to comment.