diff --git a/src/api/responses/balanceproof.spec.ts b/src/api/responses/balanceproof.spec.ts index 1b48a7a9..7222e5c3 100644 --- a/src/api/responses/balanceproof.spec.ts +++ b/src/api/responses/balanceproof.spec.ts @@ -8,7 +8,7 @@ import { Address } from "#erdstall/ledger"; import { Erdstall, Erdstall__factory, -} from "#erdstall/ledger/backend/contracts"; +} from "#erdstall/ledger/backend/ethereum/contracts"; import * as test from "#erdstall/test"; import { Environment, setupEnv } from "#erdstall/test/ledger"; diff --git a/src/api/transactions/trade.ts b/src/api/transactions/trade.ts index 35ec8f0e..d9d5a0b9 100644 --- a/src/api/transactions/trade.ts +++ b/src/api/transactions/trade.ts @@ -11,7 +11,8 @@ import { } from "#erdstall/export/typedjson"; import { ABIEncoder, ABIPacked } from "#erdstall/api/util"; import { Signature } from "#erdstall/api"; -import { Signer, utils } from "ethers"; +import { utils } from "ethers"; +import { Signer } from "#erdstall/ledger/backend"; @jsonObject export class TradeFees { diff --git a/src/api/transactions/transaction.ts b/src/api/transactions/transaction.ts index 9d17d153..8b7d5c9f 100644 --- a/src/api/transactions/transaction.ts +++ b/src/api/transactions/transaction.ts @@ -12,8 +12,9 @@ import { Serializable, jsonBigIntMember, } from "#erdstall/export/typedjson"; -import { utils, Signer } from "ethers"; +import { utils } from "ethers"; import { ETHZERO } from "#erdstall/ledger/assets"; +import { Signer } from "#erdstall/ledger/backend"; const transactionImpls = new Map>(); const transactionTypeName = "Transaction"; diff --git a/src/client.spec.ts b/src/client.spec.ts new file mode 100644 index 00000000..c77eaa04 --- /dev/null +++ b/src/client.spec.ts @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +"use strict"; + +import { ErdstallSession, ErdstallClient, Client, Session } from "#erdstall"; +import { expect } from "chai"; + +describe("ErdstallClient", () => { + const enclaveConn = new URL("http://localhost:8080"); + const cl: ErdstallClient<["ethereum", "substrate"]> = new Client< + ["ethereum", "substrate"] + >( + enclaveConn, + { + backend: "ethereum", + encConn: enclaveConn, + provider: {} as any, + }, + { + backend: "substrate", + arg: 420, + }, + ); + + cl.on("Withdrawn", (e) => {}); +}); + +describe("ErdstallSession", () => { + const singleSession: ErdstallSession<["ethereum"]> = new Session< + ["ethereum"] + >({ + backend: "ethereum", + encConn: new URL("http://localhost:8545"), + provider: {} as any, + signer: {} as any, + address: {} as any, + }); + + const _addr = singleSession.erdstall(); + + const multiSession = new Session<["ethereum", "substrate"]>( + { + backend: "ethereum", + encConn: new URL("http://localhost:8545"), + provider: {} as any, + signer: {} as any, + address: {} as any, + }, + { + backend: "substrate", + arg: 420, + }, + ); + + const _multiAddr = multiSession.erdstall(); + + singleSession.on("Withdrawn", (e) => {}); +}); diff --git a/src/e2e/sdk_actions.ts b/src/e2e/sdk_actions.ts index 27135db2..b368cb32 100644 --- a/src/e2e/sdk_actions.ts +++ b/src/e2e/sdk_actions.ts @@ -8,7 +8,7 @@ import { Session, Client } from "#erdstall"; import { PerunArt__factory, PerunToken__factory, -} from "#erdstall/ledger/backend/contracts"; +} from "#erdstall/ledger/backend/ethereum/contracts"; import { Transfer, Mint, @@ -19,9 +19,17 @@ import { import { ethers, utils } from "ethers"; import { PERUN_ADDR, PART_ADDR } from "./parameters"; +import { + mkDefaultEthereumClientConstructor, + mkDefaultEthereumSessionConstructor, +} from "#erdstall/ledger/backend/ethereum"; +import { SubstrateClient } from "#erdstall/ledger/backend/substrate"; export type SDKActions = typeof sdkActions; +// The list of backends used for this test trace. +export type TestBackends = ["ethereum", "substrate"]; + // sdkActions shows how to use the SDK for each step in a hypothetical // scenario. Each key stands for one step a user might do when acting within // the Erdstall system and shows how to assemble required parameter structures @@ -38,7 +46,7 @@ export const sdkActions = { nodeUrl: string, signer: ethers.Signer, operatorUrl: URL, - ): Promise => { + ): Promise> => { // First we need a provider which allows the SDK to communicate with the // underlying ledger. In the case of creating an `ErdstallClient` simply // creating the provider or querying it from the environment (e.g. MetaMask @@ -47,7 +55,16 @@ export const sdkActions = { // Using the provider and the URL for the operator allows creating an // `ErdstallClient`. - const client = new Client(provider, operatorUrl); + // const client = new Client(provider, operatorUrl); + const client = new Client( + operatorUrl, + mkDefaultEthereumClientConstructor(provider), + { + backend: "substrate", + arg: 42, + initializer: (_c) => new SubstrateClient(42), + }, + ); // One could use this readonly client to listen for various events within // Erdstall by registering callbacks before issuing the // `client.initialize()` call. @@ -61,7 +78,18 @@ export const sdkActions = { // calling the `provider.getSigner()` method (if the user has granted // access to your app using his account). const userAddr = Address.fromString(await signer.getAddress()); - const session = new Session(userAddr, signer, operatorUrl); + // const session = new Session(userAddr, signer, operatorUrl); + const session = new Session( + userAddr, + operatorUrl, + signer, + mkDefaultEthereumSessionConstructor(signer), + { + backend: "substrate", + arg: 42, + initializer: (_c) => new SubstrateClient(42), + }, + ); // We could now proactively set eventhandlers in place: // @@ -93,6 +121,11 @@ export const sdkActions = { }); // (Onchain) Erdstall handlers: + // + // The origin of each on-chain event is tagged in each event under the + // event.source field. + // If your Session is defined as `Session<["ethereum" | "substrate"]>`, the + // `event.source` field has the type `"ethereum" | "substrate"`. session.on("TokenTypeRegistered", (_tokenTypeRegisteredEvent) => { // A new token type with its token holder contract was registered on the // Erdstall smart contract. @@ -124,7 +157,7 @@ export const sdkActions = { return session; }, - initialize: async (session: Session) => { + initialize: async (session: Session) => { // Initialize the session. This connects us to the operator and grants us // the ability to extend our subscriptions in the next step. await session.initialize(); @@ -132,11 +165,15 @@ export const sdkActions = { // Subscribing ensures that the `ErdstallClient` or `ErdstallSession` // receives events for phaseshifts, balanceproofs and tx-receipts. - subscribe: async (session: Session) => { + subscribe: async (session: Session) => { return session.subscribeSelf(); }, - deposit: async (session: Session, ethAmount: bigint, prnAmount: bigint) => { + deposit: async ( + session: Session, + ethAmount: bigint, + prnAmount: bigint, + ) => { // Erdstall balances are abstract `Assets`. These assets contain individual // assets of the `Asset` type. Currently the SDK supports two types of assets: // @@ -157,6 +194,7 @@ export const sdkActions = { ); const { stages, numStages: _numStages } = await session.deposit( + "ethereum", depositBal, ); // Depositing is a multi-stage process. ERC20 tokens like PRN have to be @@ -184,8 +222,8 @@ export const sdkActions = { }, offchainTransfer: async ( - alice: Session, - bob: Session, + alice: Session, + bob: Session, prnAmount: bigint, ): Promise => { const amount = new Assets({ @@ -253,7 +291,7 @@ export const sdkActions = { }); }, - leave: async (session: Session) => { + leave: async (session: Session) => { // `leave` is a convenience function for first `exit`ing the Erdstall // system and afterwards `withdrawing` all available funds for the user. // So alternatively, if more control is required: @@ -265,13 +303,13 @@ export const sdkActions = { // -- callback! // // const { stages } = await session.withdraw(exitProof); - const { stages } = await session.leave(); + const { stages } = await session.leave("ethereum"); for await (const [_name, stage] of stages) { await stage.wait(); } }, - mint: async (session: Session, nftID: bigint) => { + mint: async (session: Session, nftID: bigint) => { // Offchain minting is simply a matter of passing an ERC721 contract // address and an ID (unique!). Currently it is possible to mint for any // contract offchain which is registered in Erdstall and counts as an @@ -280,8 +318,8 @@ export const sdkActions = { }, trade: async ( - charlie: Session, - dagobert: Session, + charlie: Session, + dagobert: Session, charlieNft: bigint, wantedPrnAmount: bigint, ) => { @@ -324,7 +362,7 @@ export const sdkActions = { return (await dagobert.acceptTrade(tradeOffer)).receipt; }, - burn: async (dagobert: Session, formerCharlieNft: bigint) => { + burn: async (dagobert: Session, formerCharlieNft: bigint) => { // Burning a NFT requires being the owner of it. Since Charlie transferred // his NFT in the step before, Dagobert is now able to do as he pleases. // Maybe he was not a fan of monkeys and now wants some lit whales. @@ -337,10 +375,10 @@ export const sdkActions = { }, leavingAndSeeFundsOnchain: async function ( - session: Session, + session: Session, provider: ethers.providers.Provider, ) { - const { stages } = await session.leave(); + const { stages } = await session.leave("ethereum"); for await (const [, stage] of stages) { await stage.wait(); } @@ -354,16 +392,18 @@ export const sdkActions = { // Erdstall related contracts, which frees you of the burden to come up // with bindings on your own. - // The SDK provides a convenience field for on chain calls which will grow - // over time. E.g. - [PERUN_ADDR, PART_ADDR] - .map((addr) => addr.toString()) - .map((addr) => { - session.onChainQuerier.queryTokensOwnedByAddress( - addr, - session.address.toString(), - ); - }); + // TODO: REINTRODUCE + // // The SDK provides a convenience field for on chain calls which will grow + // // over time. E.g. + // [PERUN_ADDR, PART_ADDR] + // .map((addr) => addr.toString()) + // .map((addr) => { + // session.onChainQuerier.queryTokensOwnedByAddress( + // "ethereum", + // addr, + // session.address.toString(), + // ); + // }); // The tokenProvider can be used to query information about tokens related // to Erdstall. The necessary `erdstallAddr` for these calls can be diff --git a/src/e2e/test_harness.ts b/src/e2e/test_harness.ts index 16031198..3362f779 100644 --- a/src/e2e/test_harness.ts +++ b/src/e2e/test_harness.ts @@ -11,7 +11,7 @@ import * as test from "#erdstall/test"; import { PerunArt__factory, PerunToken__factory, -} from "#erdstall/ledger/backend/contracts"; +} from "#erdstall/ledger/backend/ethereum/contracts"; import { withTimeout } from "#erdstall/utils"; import { ethers, utils } from "ethers"; @@ -91,9 +91,9 @@ export function endToEndTestHarness(sdkActions: SDKActions) { }); after(async () => erdstallProcessTerminate); - const sessions: Session[] = []; + const sessions: Session<["ethereum", "substrate"]>[] = []; const forSessionsDo = async ( - action: (client: Session) => Promise, + action: (client: Session<["ethereum", "substrate"]>) => Promise, range: { from?: number; upto?: number } = {}, ) => { return Promise.all( diff --git a/src/ledger/address.ts b/src/ledger/address.ts index 698d3239..c4c7b060 100644 --- a/src/ledger/address.ts +++ b/src/ledger/address.ts @@ -6,6 +6,14 @@ import { jsonObject } from "#erdstall/export/typedjson"; import { equalArray } from "#erdstall/utils/arrays"; import { ABIValue, customJSON } from "#erdstall/api/util"; +// // Map key +// TODO: Substrate uses Pallets, we do not have an address for each pallet? +// type AddrKey struct { +// Type string `json:"type"` // addr.Type() +// Key string `json:"key"` // addr.Key() +// } +// TODO: Switch over type, parse key as expected by backends. + /** * This class implements an address representation and is used within the SDK * wherever an address is required. diff --git a/src/ledger/assets/assets.ts b/src/ledger/assets/assets.ts index 5cc9c12d..fa1a0787 100644 --- a/src/ledger/assets/assets.ts +++ b/src/ledger/assets/assets.ts @@ -8,7 +8,7 @@ import { ABIValue, customJSON } from "#erdstall/api/util"; import { ErdstallToken } from "#erdstall/api/responses"; import { Address, addressKey } from "#erdstall/ledger"; import { TokenProvider } from "#erdstall/ledger/backend"; -import { Erdstall } from "#erdstall/ledger/backend/contracts"; +import { Erdstall } from "#erdstall/ledger/backend/ethereum/contracts"; import { decodePackedAmount } from "./amount"; import { Tokens, decodePackedIds } from "./tokens"; import { TokenType } from "./asset"; @@ -160,7 +160,7 @@ function isProperSubset( export async function decodePackedAssets( erdstall: Erdstall, - tokenProvider: Pick, + tokenProvider: Pick, "tokenTypeOf">, values: [string, string][], ): Promise { const assets = new Assets(); diff --git a/src/ledger/backend/index.ts b/src/ledger/backend/index.ts index cbe4bd8f..e2768ae1 100644 --- a/src/ledger/backend/index.ts +++ b/src/ledger/backend/index.ts @@ -1,10 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 "use strict"; -export * from "./readconn"; -export * from "./writeconn"; -export * from "./contracts"; -export * from "./contracts_deposit"; -export * from "./tokencache"; -export * from "./tokenmanager"; +export * from "./tokenprovider"; +export * from "./backends"; export * from "./metadata"; +export * from "./reader"; +export * from "./writer"; +export * from "./signer"; diff --git a/src/ledger/backend/metadata.spec.ts b/src/ledger/backend/metadata.spec.ts index 1e7f00b7..7eabe0a2 100644 --- a/src/ledger/backend/metadata.spec.ts +++ b/src/ledger/backend/metadata.spec.ts @@ -6,7 +6,7 @@ import { Client, ErdstallClient } from "#erdstall"; import { ClientConfig } from "#erdstall/api/responses"; import { Enclave } from "#erdstall/enclave"; import { Address } from "#erdstall/ledger"; -import { PerunArt__factory } from "#erdstall/ledger/backend/contracts"; +import { PerunArt__factory } from "#erdstall/ledger/backend/ethereum/contracts"; import * as test from "#erdstall/test"; import { Environment, setupEnv, PERUNART_URI } from "#erdstall/test/ledger"; import nock from "nock"; diff --git a/src/ledger/backend/tokencache.ts b/src/ledger/backend/tokencache.ts index 05723974..3805ccb7 100644 --- a/src/ledger/backend/tokencache.ts +++ b/src/ledger/backend/tokencache.ts @@ -1,276 +1,2 @@ // SPDX-License-Identifier: Apache-2.0 "use strict"; - -import { Signer } from "ethers"; -import { Address, addressKey } from "#erdstall/ledger"; -import { TokenRegistered, TokenTypeRegistered } from "#erdstall/ledger"; -import { TokenType, requireTokenType, ETHZERO } from "#erdstall/ledger/assets"; -import { Erdstall, ERC20__factory, ERC721__factory } from "./contracts"; - -interface Responder { - symbol(): Promise; -} - -/** - * Describes an entity with the ability to query token information related to - * Erdstall and its onchain contracts. - */ -export interface TokenProvider { - /** - * Sets the locally cached token type for the given token address. - * - * @param tokenAddr - The address of the token to update. - * @param ttype - The token type. - */ - setType(tokenAddr: string, ttype: TokenType): void; - /** - * Retrieves the address of the token holder contract for the given token - * type related to the given Erdstall contract. - * - * @param erdstall - The Erdstall contract. - * @param ttype - The token type which is handled by some holder contract. - * @throws An error when no token holder contract is registered for the given - * token type. - * @returns The address of the holder contract in string representation. - */ - tokenHolderFor(erdstall: Erdstall, ttype: TokenType): Promise; - /** - * Retrieves the token type for a given token address related to the given - * Erdstall contract. - * - * @param erdstall - The Erdstall contract. - * @param tokenAddr - The address of a token contract. - * @throws An error when the given token address is not registered with the - * Erdstall contract. - * @returns The token type of the given token address. - */ - tokenTypeOf(erdstall: Erdstall, tokenAddr: string): Promise; - /** - * Finds the address of the first registered token contract in Erdstall, - * which has the given symbol as its symbol according to the ERC20Detailed - * specification. - * - * @param erdstall - The Erdstall contract. - * @param symbol - The symbol to search for. - * @param fromBlock - When omitted the starting block from when the Erdstall - * contract was deployed will be used. - * @returns The address when a token contract matching the symbol could be - * found, otherwise undefined. - * - * @remarks - * [ERC20Detailed](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC20/ERC20Detailed.sol) - */ - findRegisteredTokenWithSymbol( - erdstall: Erdstall, - symbol: string, - fromBlock?: number, - ): Promise
; - /** - * Queries the registered token types on the given Erdstall contract. - * - * @param erdstall - The Erdstall contract. - * @param fromBlock - When omitted the starting block from when the Erdstall - * contract was deployed will be used. - * @returns A list of TokenTypeRegistered events, which might be empty if no - * token types were registered. - */ - queryRegisteredTokenTypes( - erdstall: Erdstall, - fromBlock?: number, - ): Promise; - /** - * Queries the registered tokens on the given Erdstall contract. - * - * @param erdstall - The Erdstall contract. - * @param fromBlock - When omitted the starting block from when the Erdstall - * contract was deployed will be used. - * @returns A list of TokenRegistered events, which might be empty if no - * tokens were registered. - */ - queryRegisteredTokens( - erdstall: Erdstall, - fromBlock?: number, - ): Promise; -} - -type TokenTypes = Map; -type TokenHolders = Map; - -export class TokenFetcher implements TokenProvider { - readonly typeCache: TokenTypes; - readonly holderCache: TokenHolders; - private bigbang?: number; - - constructor() { - this.typeCache = new Map(); - this.typeCache.set(ETHZERO, "ETH"); - this.holderCache = new Map(); - } - - setType(tokenAddr: string, ttype: TokenType) { - this.typeCache.set(addressKey(tokenAddr), ttype); - } - - async tokenHolderFor( - erdstall: Erdstall, - ttype: TokenType, - ): Promise { - if (this.holderCache.has(ttype)) { - return this.holderCache.get(ttype)!; - } - - await this.queryRegisteredTokenTypes(erdstall, this.bigbang); - - if (!this.holderCache.has(ttype)) { - throw new Error( - "no holder for the given tokentype registered on erdstall", - ); - } - return this.holderCache.get(ttype)!; - } - - async tokenTypeOf( - erdstall: Erdstall, - tokenAddr: string, - ): Promise { - tokenAddr = addressKey(tokenAddr); - if (this.typeCache.has(tokenAddr)) { - return this.typeCache.get(tokenAddr)!; - } - - const ttype = await this.queryTokenType(erdstall, tokenAddr); - if (!ttype) { - return Promise.reject( - new Error(`given token not registered: ${tokenAddr}`), - ); - } - - this.typeCache.set(tokenAddr, ttype!); - return ttype!; - } - - async queryTokenType( - erdstall: Erdstall, - tokenAddr: string, - ): Promise { - tokenAddr = addressKey(tokenAddr); - await this.queryRegisteredTokens(erdstall, this.bigbang); - return this.typeCache.get(tokenAddr); - } - - async findRegisteredTokenWithSymbol( - erdstall: Erdstall, - symbol: string, - fromBlock?: number, - ): Promise
{ - const from = fromBlock - ? fromBlock - : (await erdstall.bigBang()).toNumber(); - - const registeredTokens = await this.queryRegisteredTokens( - erdstall, - from, - ); - - for (const ev of registeredTokens) { - try { - const token = this.resolveTokenType( - erdstall.signer, - requireTokenType(ev.tokenType), - ev.token, - ); - const sym = await token.symbol(); - if (sym === symbol) { - return ev.token; - } - } catch { - return; - } - } - return; - } - - async queryRegisteredTokenTypes( - erdstall: Erdstall, - fromBlock?: number, - ): Promise { - const from = fromBlock - ? fromBlock - : (await erdstall.bigBang()).toNumber(); - - const filter = erdstall.filters.TokenTypeRegistered(null, null); - return erdstall.queryFilter(filter, from).then((ev) => { - return ev.map((entry) => { - const ttype = requireTokenType(entry.args.tokenType); - const tokenHolder = entry.args.tokenHolder; - - if (!this.holderCache.has(ttype)) { - this.holderCache.set(ttype, tokenHolder); - } - - return { - tokenType: ttype, - tokenHolder: Address.fromString(tokenHolder), - }; - }); - }); - } - - // queryRegisteredTokens queries the registered tokens from the - // erdstall-contract. It also updates the tokentype cache accordingly. - async queryRegisteredTokens( - erdstall: Erdstall, - fromBlock?: number, - ): Promise { - const from = fromBlock - ? fromBlock - : (await erdstall.bigBang()).toNumber(); - - const filter = erdstall.filters.TokenRegistered(null, null, null); - return erdstall.queryFilter(filter, from).then((ev) => { - return ev.map((entry) => { - const token = addressKey(entry.args.token); - const ttype = requireTokenType(entry.args.tokenType); - const tokenHolder = entry.args.tokenHolder; - - if (!this.typeCache.has(token)) { - this.setType(token, ttype); - } - - if (!this.holderCache.has(ttype)) { - this.holderCache.set(ttype, tokenHolder); - } - - return { - token: Address.fromString(token), - tokenType: ttype, - tokenHolder: Address.fromString(tokenHolder), - }; - }); - }); - } - - resolveTokenType( - signer: Signer, - ttype: TokenType, - token: string | Address, - ): Responder { - token = addressKey(token); - switch (ttype) { - case "ERC20": - return ERC20__factory.connect(token, signer); - case "ERC721": - return ERC721__factory.connect(token, signer); - case "ERC721Mintable": - return ERC721__factory.connect(token, signer); - case "ETH": - return { - symbol: async function (): Promise { - return "ETH"; - }, - }; - default: - throw Error("not implemented"); - } - } -} diff --git a/src/ledger/index.ts b/src/ledger/index.ts index 00c4b505..4e1268fd 100644 --- a/src/ledger/index.ts +++ b/src/ledger/index.ts @@ -5,6 +5,5 @@ export * from "./account"; export * from "./address"; export * from "./event"; export * from "./onChainQuerier"; - -export * from "./backend/readconn"; -export * from "./backend/writeconn"; +export * from "./backend/writer"; +export * from "./backend/reader"; diff --git a/src/test/ledger/env.ts b/src/test/ledger/env.ts index 574fa775..8c588e73 100644 --- a/src/test/ledger/env.ts +++ b/src/test/ledger/env.ts @@ -9,15 +9,17 @@ import { deployContract, MockProvider } from "ethereum-waffle"; import { Erdstall__factory, PerunArt__factory, -} from "#erdstall/ledger/backend/contracts"; +} from "#erdstall/ledger/backend/ethereum/contracts"; import { ETHZERO } from "#erdstall/ledger/assets"; -import peruntokenABI from "../../ledger/backend/contracts/abi/PerunToken.json"; -import erdstallABI from "../../ledger/backend/contracts/abi/Erdstall.json"; -import erc20holderABI from "../../ledger/backend/contracts/abi/ERC20Holder.json"; -import erc721holderABI from "../../ledger/backend/contracts/abi/ERC721Holder.json"; -import ethholderABI from "../../ledger/backend/contracts/abi/ETHHolder.json"; -import perunArtABI from "../../ledger/backend/contracts/abi/PerunArt.json"; +import peruntokenABI from "../../ledger/backend/ethereum/contracts/abi/PerunToken.json"; +import erdstallABI from "../../ledger/backend/ethereum/contracts/abi/Erdstall.json"; +import erc20holderABI from "../../ledger/backend/ethereum/contracts/abi/ERC20Holder.json"; +import erc721holderABI from "../../ledger/backend/ethereum/contracts/abi/ERC721Holder.json"; +import ethholderABI from "../../ledger/backend/ethereum/contracts/abi/ETHHolder.json"; +import perunArtABI from "../../ledger/backend/ethereum/contracts/abi/PerunArt.json"; + +import {} from "#erdstall/ledger/backend"; // ethereum-waffle does not expose this type... type _params = ConstructorParameters; diff --git a/src/test/mocks.ts b/src/test/mocks.ts index 8b0a9be1..bf6cc8f5 100644 --- a/src/test/mocks.ts +++ b/src/test/mocks.ts @@ -33,44 +33,61 @@ import { } from "#erdstall/api/calls"; import { Transaction } from "#erdstall/api/transactions"; import { Address, Account, OnChainQuerier } from "#erdstall/ledger"; -import { - NFTMetadata, - TokenFetcher, - TokenProvider, -} from "#erdstall/ledger/backend"; +import { NFTMetadata, TokenProvider } from "#erdstall/ledger/backend"; +import { TokenFetcher } from "#erdstall/ledger/backend/ethereum"; import { Assets, Tokens } from "#erdstall/ledger/assets"; import { EnclaveProvider } from "#erdstall/enclave"; -export class MockWatcher implements Watcher { - private txReceiptHandler!: ErdstallEventHandler<"receipt">; - private exitProofHandler!: ErdstallEventHandler<"exitproof">; - private balanceProofHandler!: ErdstallEventHandler<"proof">; - private phaseShiftHandler!: ErdstallEventHandler<"phaseshift">; +export class MockWatcher implements Watcher<["ethereum"]> { + private txReceiptHandler!: ErdstallEventHandler<"receipt", "ethereum">; + private exitProofHandler!: ErdstallEventHandler<"exitproof", "ethereum">; + private balanceProofHandler!: ErdstallEventHandler<"proof", "ethereum">; + private phaseShiftHandler!: ErdstallEventHandler<"phaseshift", "ethereum">; - on(ev: T, cb: ErdstallEventHandler): void { + on( + ev: T, + cb: ErdstallEventHandler, + ): void { switch (ev) { case "receipt": - this.txReceiptHandler = cb as ErdstallEventHandler<"receipt">; + this.txReceiptHandler = cb as ErdstallEventHandler< + "receipt", + "ethereum" + >; break; case "proof": - this.balanceProofHandler = cb as ErdstallEventHandler<"proof">; + this.balanceProofHandler = cb as ErdstallEventHandler< + "proof", + "ethereum" + >; break; case "exitproof": - this.exitProofHandler = cb as ErdstallEventHandler<"exitproof">; + this.exitProofHandler = cb as ErdstallEventHandler< + "exitproof", + "ethereum" + >; break; case "phaseshift": - this.phaseShiftHandler = - cb as ErdstallEventHandler<"phaseshift">; + this.phaseShiftHandler = cb as ErdstallEventHandler< + "phaseshift", + "ethereum" + >; break; default: throw new Error(`MockWatcher: unsupported event "${ev}"`); } } - once(_ev: T, _cb: ErdstallEventHandler): void { + once( + _ev: T, + _cb: ErdstallEventHandler, + ): void { throw new Error("not implemented"); } - off(_ev: T, _cb: ErdstallEventHandler): void { + off( + _ev: T, + _cb: ErdstallEventHandler, + ): void { throw new Error("not implemented"); } removeAllListeners(): void { @@ -130,9 +147,12 @@ export class MockWatcher implements Watcher { } } -export class MockClient extends MockWatcher implements ErdstallClient { - readonly tokenProvider: TokenProvider; - readonly onChainQuerier: OnChainQuerier; +export class MockClient + extends MockWatcher + implements ErdstallClient<["ethereum"]> +{ + readonly tokenProvider: TokenProvider<"ethereum">; + readonly onChainQuerier: OnChainQuerier<["ethereum"]>; private readonly contract: Address; private metadata: Map; @@ -157,7 +177,11 @@ export class MockClient extends MockWatcher implements ErdstallClient { this.metadata.set(`${token.key}:${id}`, metadata); } - async getNftMetadata(token: Address, id: bigint): Promise { + async getNftMetadata( + _backend: "ethereum", + token: Address, + id: bigint, + ): Promise { const res = this.metadata.get(`${token.key}:${id}`); if (!res) { return Promise.reject( @@ -167,8 +191,9 @@ export class MockClient extends MockWatcher implements ErdstallClient { return res; } - erdstall(): Address { - return this.contract; + erdstall(): { chain: "ethereum"; address: Address } { + throw new Error("not implemented"); + // return this.contract; } } @@ -308,9 +333,10 @@ function newTxReceiptResult( return new Result(id, txr); } -class MockOnChainQuerier implements OnChainQuerier { +class MockOnChainQuerier implements OnChainQuerier<["ethereum"]> { constructor() {} async queryTokensOwnedByAddress( + _backend: "ethereum" | "ethereum"[], _token: string, _address: string, ): Promise { diff --git a/src/utils/receipt_dispatcher.ts b/src/utils/receipt_dispatcher.ts index c39fa232..4dec00c0 100644 --- a/src/utils/receipt_dispatcher.ts +++ b/src/utils/receipt_dispatcher.ts @@ -1,28 +1,23 @@ import { TxReceipt } from "#erdstall/api/responses"; -import { LedgerReader, LedgerWriter } from "#erdstall/ledger"; export class ReceiptDispatcher { private pendingReceiptDispatchers: Map void>; - erdstallConn?: LedgerReader | LedgerWriter; - - constructor(erdstallConn?: LedgerReader | LedgerWriter) { + constructor() { this.pendingReceiptDispatchers = new Map< string, (value: TxReceipt) => void >(); - this.erdstallConn = erdstallConn; } register(hash: string): Promise { - const pendingReceipt = new Promise((resolve, reject) => { + const pendingReceipt = new Promise((resolve, _reject) => { this.pendingReceiptDispatchers.set(hash, resolve); }); return pendingReceipt; } watch(receipt: TxReceipt): void { - if (!this.erdstallConn) return; const hash = receipt.tx.hash(); const dispatch = this.pendingReceiptDispatchers.get(hash); if (dispatch) {