diff --git a/.eslintignore b/.eslintignore index f6b3e6b..96c7c47 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ -**/dist -**/build +dist/ +build/ +temp/ diff --git a/integration/testHelpers.ts b/integration/testHelpers.ts index 49997e3..b5bfa65 100644 --- a/integration/testHelpers.ts +++ b/integration/testHelpers.ts @@ -4,41 +4,39 @@ import Web3 from 'web3'; import { TestConfig } from './testConfig'; import { - createDefaultTokenBridge, + loggerProvider, + TokenBridge, + TaquitoContractTezosBridgeBlockchainService, + Web3EtherlinkBridgeBlockchainService, + DefaultDataProvider, BridgeTokenTransferKind, BridgeTokenTransferStatus, LogLevel, - type TokenBridge, type DefaultTokenBridgeOptions, type TezosToken, type EtherlinkToken, type BridgeTokenTransfer, type PendingBridgeTokenDeposit, type CreatedBridgeTokenDeposit, type FinishedBridgeTokenDeposit, type PendingBridgeTokenWithdrawal, type CreatedBridgeTokenWithdrawal, - type SealedBridgeTokenWithdrawal, type FinishedBridgeTokenWithdrawal + type SealedBridgeTokenWithdrawal, type FinishedBridgeTokenWithdrawal, + type DefaultDataProviderOptions, } from '../src'; -const tezosOperationRegex = /^o/; +const depositIdRegex = /^o[0-9a-zA-Z]{50}_\d+_\d+$/; +const withdrawalIdRegex = /^0x[0-9a-f]{64}_\d+$/; +const tezosOperationRegex = /^o[0-9a-zA-Z]{50}$/; const etherlinkOperationRegex = /^0x[0-9a-f]{64}$/; interface CreateTestTokenBridgeParams { testConfig: TestConfig, tezosToolkit?: TezosToolkit, etherlinkToolkit?: Web3, - overrideOptions?: Partial + overriddenDefaultDataProviderOptions?: Partial } -export const createTestTokenBridge = ({ testConfig, tezosToolkit, etherlinkToolkit, overrideOptions }: CreateTestTokenBridgeParams): TokenBridge => { +export const createTestTokenBridge = ({ testConfig, tezosToolkit, etherlinkToolkit, overriddenDefaultDataProviderOptions }: CreateTestTokenBridgeParams) => { tezosToolkit = tezosToolkit || createTezosToolkitWithSigner(testConfig.tezosRpcUrl, testConfig.tezosAccountPrivateKey); etherlinkToolkit = etherlinkToolkit || createEtherlinkToolkitWithSigner(testConfig.etherlinkRpcUrl, testConfig.etherlinkAccountPrivateKey); - return createDefaultTokenBridge({ - logging: { - logLevel: LogLevel.Debug - }, - tezos: { - toolkit: tezosToolkit, - rollupAddress: testConfig.tezosRollupAddress - }, - etherlink: { - toolkit: etherlinkToolkit - }, + loggerProvider.setLogLevel(LogLevel.Debug); + + const defaultDataProvider = new DefaultDataProvider({ dipDup: { baseUrl: testConfig.dipDupBaseUrl, webSocketApiBaseUrl: testConfig.dipDupBaseUrl.replace('https', 'wss'), @@ -76,7 +74,22 @@ export const createTestTokenBridge = ({ testConfig, tezosToolkit, etherlinkToolk } }, ], - ...overrideOptions + ...overriddenDefaultDataProviderOptions + }); + + return new TokenBridge({ + tezosBridgeBlockchainService: new TaquitoContractTezosBridgeBlockchainService({ + tezosToolkit, + smartRollupAddress: testConfig.tezosRollupAddress + }), + etherlinkBridgeBlockchainService: new Web3EtherlinkBridgeBlockchainService({ + web3: etherlinkToolkit + }), + bridgeDataProviders: { + transfers: defaultDataProvider, + balances: defaultDataProvider, + tokens: defaultDataProvider, + } }); }; @@ -135,6 +148,7 @@ export const expectCreatedDeposit = ( } ) => { expect(createdBridgeTokenDeposit).toMatchObject({ + id: expect.stringMatching(depositIdRegex), kind: BridgeTokenTransferKind.Deposit, status: BridgeTokenTransferStatus.Created, source: params.source, @@ -142,9 +156,10 @@ export const expectCreatedDeposit = ( tezosOperation: { blockId: expect.any(Number), hash: expect.stringMatching(tezosOperationRegex), + counter: expect.any(Number), + nonce: expect.any(Number), amount: params.amount, token: params.tezosToken, - fee: expect.any(BigInt), timestamp: expect.any(String), } }); @@ -162,6 +177,7 @@ export const expectFinishedDeposit = ( } ) => { expect(finishedBridgeTokenDeposit).toMatchObject({ + id: expect.stringMatching(depositIdRegex), kind: BridgeTokenTransferKind.Deposit, status: BridgeTokenTransferStatus.Finished, source: params.source, @@ -169,17 +185,18 @@ export const expectFinishedDeposit = ( tezosOperation: { blockId: expect.any(Number), hash: expect.stringMatching(tezosOperationRegex), + counter: expect.any(Number), + nonce: expect.any(Number), amount: params.inAmount, token: params.tezosToken, - fee: expect.any(BigInt), timestamp: expect.any(String), }, etherlinkOperation: { blockId: expect.any(Number), hash: expect.stringMatching(etherlinkOperationRegex), + logIndex: expect.any(Number), amount: params.outAmount, token: params.etherlinkToken, - fee: expect.any(BigInt), timestamp: expect.any(String), } }); @@ -218,6 +235,7 @@ export const expectCreatedWithdrawal = ( } ) => { expect(createdBridgeTokenWithdrawal).toMatchObject({ + id: expect.stringMatching(withdrawalIdRegex), kind: BridgeTokenTransferKind.Withdrawal, status: BridgeTokenTransferStatus.Created, source: params.source, @@ -225,9 +243,9 @@ export const expectCreatedWithdrawal = ( etherlinkOperation: { blockId: expect.any(Number), hash: expect.stringMatching(etherlinkOperationRegex), + logIndex: expect.any(Number), amount: params.amount, token: params.etherlinkToken, - fee: expect.any(BigInt), timestamp: expect.any(String), }, rollupData: { @@ -247,6 +265,7 @@ export const expectSealedWithdrawal = ( } ) => { expect(sealedBridgeTokenWithdrawal).toMatchObject({ + id: expect.stringMatching(withdrawalIdRegex), kind: BridgeTokenTransferKind.Withdrawal, status: BridgeTokenTransferStatus.Sealed, source: params.source, @@ -254,16 +273,16 @@ export const expectSealedWithdrawal = ( etherlinkOperation: { blockId: expect.any(Number), hash: expect.stringMatching(etherlinkOperationRegex), + logIndex: expect.any(Number), amount: params.amount, token: params.etherlinkToken, - fee: expect.any(BigInt), timestamp: expect.any(String), }, rollupData: { outboxMessageIndex: expect.any(Number), outboxMessageLevel: expect.any(Number), commitment: expect.any(String), - proof: expect.any(String) + proof: expect.any(String), } }); }; @@ -280,6 +299,7 @@ export const expectFinishedWithdrawal = ( } ) => { expect(finishedBridgeTokenWithdrawal).toMatchObject({ + id: expect.stringMatching(withdrawalIdRegex), kind: BridgeTokenTransferKind.Withdrawal, status: BridgeTokenTransferStatus.Finished, source: params.source, @@ -287,17 +307,18 @@ export const expectFinishedWithdrawal = ( etherlinkOperation: { blockId: expect.any(Number), hash: expect.stringMatching(etherlinkOperationRegex), + logIndex: expect.any(Number), amount: params.inAmount, token: params.etherlinkToken, - fee: expect.any(BigInt), timestamp: expect.any(String), }, tezosOperation: { blockId: expect.any(Number), hash: expect.stringMatching(tezosOperationRegex), + counter: expect.any(Number), + nonce: null, amount: params.outAmount, token: params.tezosToken, - fee: expect.any(BigInt), timestamp: expect.any(String), }, rollupData: { diff --git a/integration/tests/balances.test.ts b/integration/tests/balances.test.ts index 2025363..8eed43b 100644 --- a/integration/tests/balances.test.ts +++ b/integration/tests/balances.test.ts @@ -1,7 +1,7 @@ import { TezosToolkit } from '@taquito/taquito'; import Web3 from 'web3'; -import { type TokenBridge, type AccountTokenBalance, type AccountTokenBalances } from '../../src'; +import type { AccountTokenBalance, AccountTokenBalances } from '../../src'; import { getTestConfig, type TestConfig, type TestTokens } from '../testConfig'; import { createTezosToolkitWithSigner, createEtherlinkToolkitWithSigner, createTestTokenBridge @@ -12,7 +12,7 @@ describe('Balances', () => { let tokens: TestTokens; let tezosToolkit: TezosToolkit; let etherlinkToolkit: Web3; - let tokenBridge: TokenBridge; + let tokenBridge: ReturnType; let testTezosAccountAddress: string; let testEtherlinkAccountAddress: string; @@ -27,11 +27,11 @@ describe('Balances', () => { tokenBridge = createTestTokenBridge({ testConfig, tezosToolkit, etherlinkToolkit }); const connectedAddresses = await Promise.all([ - await tokenBridge.getTezosConnectedAddress(), - await tokenBridge.getEtherlinkConnectedAddress() + await tokenBridge.getTezosSignerAddress(), + await tokenBridge.getEtherlinkSignerAddress() ]); - testTezosAccountAddress = connectedAddresses[0]; - testEtherlinkAccountAddress = connectedAddresses[1]; + testTezosAccountAddress = connectedAddresses[0]!; + testEtherlinkAccountAddress = connectedAddresses[1]!; }); afterEach(() => { diff --git a/integration/tests/deposit.test.ts b/integration/tests/deposit.test.ts index 35c0002..4c34008 100644 --- a/integration/tests/deposit.test.ts +++ b/integration/tests/deposit.test.ts @@ -3,7 +3,7 @@ import Web3 from 'web3'; import { BridgeTokenTransferStatus, - type NativeEtherlinkToken, type NativeTezosToken, type TokenBridge + type NativeEtherlinkToken, type NativeTezosToken } from '../../src'; import { bridgeUtils } from '../../src/utils'; import { getTestConfig, type TestConfig, type TestTokens } from '../testConfig'; @@ -16,15 +16,12 @@ import { createTestTokenBridge } from '../testHelpers'; -// The Taquito Wallet API does not close some handles after tests complete. -const useWalletApi = false; - describe('Deposit', () => { let testConfig: TestConfig; let tokens: TestTokens; let tezosToolkit: TezosToolkit; let etherlinkToolkit: Web3; - let tokenBridge: TokenBridge; + let tokenBridge: ReturnType; let testTezosAccountAddress: string; let testEtherlinkAccountAddress: string; @@ -39,11 +36,11 @@ describe('Deposit', () => { tokenBridge = createTestTokenBridge({ testConfig, tezosToolkit, etherlinkToolkit }); const connectedAddresses = await Promise.all([ - await tokenBridge.getTezosConnectedAddress(), - await tokenBridge.getEtherlinkConnectedAddress() + await tokenBridge.getTezosSignerAddress(), + await tokenBridge.getEtherlinkSignerAddress() ]); - testTezosAccountAddress = connectedAddresses[0]; - testEtherlinkAccountAddress = connectedAddresses[1]; + testTezosAccountAddress = connectedAddresses[0]!; + testEtherlinkAccountAddress = connectedAddresses[1]!; }); afterEach(() => { @@ -54,7 +51,7 @@ describe('Deposit', () => { const amount = 1_000_000n; const [tezosToken, etherlinkToken]: [NativeTezosToken, NativeEtherlinkToken] = [tokens.tezos.tez, tokens.etherlink.tez]; - const depositResult = await tokenBridge.deposit(amount, tezosToken, { useWalletApi }); + const depositResult = await tokenBridge.deposit(amount, tezosToken); expectPendingDeposit(depositResult.tokenTransfer, { amount, source: testTezosAccountAddress, @@ -81,7 +78,7 @@ describe('Deposit', () => { const amount = 7n; const [tezosToken, etherlinkToken] = [tokens.tezos.ctez, tokens.etherlink.ctez]; - const depositResult = await tokenBridge.deposit(amount, tezosToken, { useWalletApi }); + const depositResult = await tokenBridge.deposit(amount, tezosToken); expectPendingDeposit(depositResult.tokenTransfer, { amount, source: testTezosAccountAddress, @@ -108,7 +105,7 @@ describe('Deposit', () => { const amount = 20n; const [tezosToken, etherlinkToken] = [tokens.tezos.usdt, tokens.etherlink.usdt]; - const depositResult = await tokenBridge.deposit(amount, tezosToken, { useWalletApi }); + const depositResult = await tokenBridge.deposit(amount, tezosToken); expectPendingDeposit(depositResult.tokenTransfer, { amount, source: testTezosAccountAddress, @@ -172,8 +169,8 @@ describe('Deposit', () => { } }); - tokenBridge.deposit(amount, tezosToken, { useWalletApi }) - .then(result => tokenBridge.stream.subscribeToTokenTransfer(result.tokenTransfer)); + tokenBridge.deposit(amount, tezosToken) + .then(result => tokenBridge.stream.subscribeToOperationTokenTransfers(result.tokenTransfer)); }); test('Deposit FA1.2 token, check the transfer status using events (subscribeToAccountTransfers)', done => { @@ -183,7 +180,7 @@ describe('Deposit', () => { let tokenTransferOperationHash: string | undefined; tokenBridge.addEventListener('tokenTransferCreated', tokenTransfer => { - if (bridgeUtils.getInitialOperationHash(tokenTransfer) !== tokenTransferOperationHash) + if (bridgeUtils.getInitialOperation(tokenTransfer).hash !== tokenTransferOperationHash) return; expectPendingDeposit(tokenTransfer, { @@ -196,7 +193,7 @@ describe('Deposit', () => { }); tokenBridge.addEventListener('tokenTransferUpdated', tokenTransfer => { - if (bridgeUtils.getInitialOperationHash(tokenTransfer) !== tokenTransferOperationHash) + if (bridgeUtils.getInitialOperation(tokenTransfer).hash !== tokenTransferOperationHash) return; if (tokenTransfer.status === BridgeTokenTransferStatus.Created) { @@ -217,7 +214,7 @@ describe('Deposit', () => { etherlinkToken }); if (!readyForDone) { - done.fail('The tokenTransferCreated event has not been fired.'); + done('The tokenTransferCreated event has not been fired.'); } done(); @@ -226,9 +223,9 @@ describe('Deposit', () => { tokenBridge.stream.subscribeToAccountTokenTransfers([testTezosAccountAddress, testEtherlinkAccountAddress]); - tokenBridge.deposit(amount, tezosToken, { useWalletApi }) + tokenBridge.deposit(amount, tezosToken) .then(depositResult => { - tokenTransferOperationHash = depositResult.depositOperation.hash; + tokenTransferOperationHash = depositResult.operationResult.hash; }); }); }); diff --git a/integration/tests/withdrawal.test.ts b/integration/tests/withdrawal.test.ts index 47cf499..a8c0f85 100644 --- a/integration/tests/withdrawal.test.ts +++ b/integration/tests/withdrawal.test.ts @@ -35,11 +35,11 @@ describe('Withdrawal', () => { tokenBridge = createTestTokenBridge({ testConfig, tezosToolkit, etherlinkToolkit }); const connectedAddresses = await Promise.all([ - await tokenBridge.getTezosConnectedAddress(), - await tokenBridge.getEtherlinkConnectedAddress() + await tokenBridge.getTezosSignerAddress(), + await tokenBridge.getEtherlinkSignerAddress() ]); - testTezosAccountAddress = connectedAddresses[0]; - testEtherlinkAccountAddress = connectedAddresses[1]; + testTezosAccountAddress = connectedAddresses[0]!; + testEtherlinkAccountAddress = connectedAddresses[1]!; }); afterEach(() => { @@ -70,7 +70,7 @@ describe('Withdrawal', () => { }); const sealedBridgeTokenWithdrawal = await tokenBridge.waitForStatus( - startWithdrawResult.tokenTransfer, + createdBridgeTokenWithdrawal, BridgeTokenTransferStatus.Sealed ); expectSealedWithdrawal(sealedBridgeTokenWithdrawal, { @@ -119,7 +119,7 @@ describe('Withdrawal', () => { }); const sealedBridgeTokenWithdrawal = await tokenBridge.waitForStatus( - startWithdrawResult.tokenTransfer, + createdBridgeTokenWithdrawal, BridgeTokenTransferStatus.Sealed ); expectSealedWithdrawal(sealedBridgeTokenWithdrawal, { diff --git a/package-lock.json b/package-lock.json index f8aa522..4e16071 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,13 +9,13 @@ "version": "0.1.3", "license": "MIT", "dependencies": { - "@taquito/utils": "^19.1.0-RC.1", + "@taquito/utils": "^19.1.0", "web3-utils": "^4.2.0", "web3-validator": "^2.0.4" }, "devDependencies": { - "@taquito/signer": "^19.1.0-RC.1", - "@taquito/taquito": "^19.1.0-RC.1", + "@taquito/signer": "^19.1.0", + "@taquito/taquito": "^19.1.0", "@types/jest": "^29.5.11", "@typescript-eslint/eslint-plugin": "^6.19.1", "@typescript-eslint/parser": "^6.19.1", @@ -33,7 +33,7 @@ "node": ">=20" }, "peerDependencies": { - "@taquito/taquito": "^19.1.0-RC.1", + "@taquito/taquito": ">=19.0.0", "web3": "^4.4.0", "ws": "^8.16.0" }, @@ -1950,9 +1950,9 @@ } }, "node_modules/@taquito/core": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/core/-/core-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-HkZsqMskL7DWpYOPaSQnHLn0Q21DK3KiT/lEeNhBcGKtvLaJjVjduZ9A6TQR+GfAO+x+CkHILjuGaH/mht69cA==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/core/-/core-19.1.0.tgz", + "integrity": "sha512-gW7rQCXnznCsTaVNF04ilS6jgzxfJWRiDm7BDC0U5FrEHxIfiRXj6dDbVtOLvcai2xBmfCghFL79lCsToVYztQ==", "dependencies": { "json-stringify-safe": "^5.0.1" }, @@ -1961,12 +1961,12 @@ } }, "node_modules/@taquito/http-utils": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-/NYSY4fVfPPnwA6pVN3TMQaoop3xU0q5CXZ7u5vmDUV2GOTtP6noUeJuvNX5pabwjJZxLwdYLWXCvXZq9qI5AQ==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-19.1.0.tgz", + "integrity": "sha512-MDvK5rHFtgpK/c4G8ttrYj5fy4lDz/MpwryrUjfcAZG/EfWdFcw6JIaFrrIrCRUp2d09+182sogaT3xzYGq6kA==", "dev": true, "dependencies": { - "@taquito/core": "^19.1.0-beta-RC.0", + "@taquito/core": "^19.1.0", "node-fetch": "^2.7.0" }, "engines": { @@ -1974,61 +1974,40 @@ } }, "node_modules/@taquito/local-forging": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-e/Lc3PqdX7POKIxDA5JiCKdLlvr8htIAzyqYhrEZMeqdGHVR2ZFjTnGxcGZiTncpN9NfhY46U/z9NT2bmqp4aw==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-19.1.0.tgz", + "integrity": "sha512-ZjucQX3Gv+MzYJmTY617ok93nPXLK/5P/Gtxolys4sETT5KaQAbxWCkaZIlJEYVXso/jF2JRUfiXzzoMLR9Q1w==", "dev": true, "dependencies": { - "@taquito/core": "^19.1.0-beta-RC.0", - "@taquito/utils": "^19.1.0-beta-RC.0", + "@taquito/core": "^19.1.0", + "@taquito/utils": "^19.1.0", "bignumber.js": "^9.1.2" }, "engines": { "node": ">=18" } }, - "node_modules/@taquito/local-forging/node_modules/@taquito/utils": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-7YmWeNVJ6Sh+Yy/Oyv/ch6ScAw8CuEoecnuJNusbkKn4eJI3xP4TEW5tqhnFrFT829Z2Wizi4kHoykz8W4I07A==", - "dev": true, - "dependencies": { - "@stablelib/blake2b": "^1.0.1", - "@stablelib/ed25519": "^1.0.3", - "@taquito/core": "^19.1.0-beta-RC.0", - "@types/bs58check": "^2.1.0", - "bignumber.js": "^9.1.2", - "blakejs": "^1.2.1", - "bs58check": "^3.0.1", - "buffer": "^6.0.3", - "elliptic": "^6.5.4", - "typedarray-to-buffer": "^4.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@taquito/michel-codec": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-z6DhwsQmyaijopQs9brw/V1mLu2j74uL8q38jY8OVS8bRKIUSvsunDBd8dJGbLARcHG2JncOGLrFZ5E631VuSw==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-19.1.0.tgz", + "integrity": "sha512-iIvNBPowx/x/rtaxCuVj1QL2jR3iaHaZHGfiN+gUa5jo+eL3YUJZSIDCiTQM3HKHVBNP+0x+OeBeuCmMcSMVog==", "dev": true, "dependencies": { - "@taquito/core": "^19.1.0-beta-RC.0" + "@taquito/core": "^19.1.0" }, "engines": { "node": ">=18" } }, "node_modules/@taquito/michelson-encoder": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-XAwUhcTzahPVk8rcipLaG/RNjTXS/KNM5eHhi7RSH+37e5fgaCiRa7+cASFlWG605/zmwg7Ro2VZVZTZTt7vbg==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-19.1.0.tgz", + "integrity": "sha512-1MG2eG6R4/UwMAzEgz8k79yWh1vfzlFfmbAQpoigC6pfDAsoc5/zaqKcdxpqrpd422TA+8LTewYFXkGBFLf/cA==", "dev": true, "dependencies": { - "@taquito/core": "^19.1.0-beta-RC.0", - "@taquito/rpc": "^19.1.0-beta-RC.0", - "@taquito/utils": "^19.1.0-beta-RC.0", + "@taquito/core": "^19.1.0", + "@taquito/rpc": "^19.1.0", + "@taquito/utils": "^19.1.0", "bignumber.js": "^9.1.2", "fast-json-stable-stringify": "^2.1.0" }, @@ -2036,67 +2015,25 @@ "node": ">=18" } }, - "node_modules/@taquito/michelson-encoder/node_modules/@taquito/utils": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-7YmWeNVJ6Sh+Yy/Oyv/ch6ScAw8CuEoecnuJNusbkKn4eJI3xP4TEW5tqhnFrFT829Z2Wizi4kHoykz8W4I07A==", - "dev": true, - "dependencies": { - "@stablelib/blake2b": "^1.0.1", - "@stablelib/ed25519": "^1.0.3", - "@taquito/core": "^19.1.0-beta-RC.0", - "@types/bs58check": "^2.1.0", - "bignumber.js": "^9.1.2", - "blakejs": "^1.2.1", - "bs58check": "^3.0.1", - "buffer": "^6.0.3", - "elliptic": "^6.5.4", - "typedarray-to-buffer": "^4.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@taquito/rpc": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-EcphrNCM3Wbyuv+CdETyrv+M9B+DHTOXluI+E99jH0H2Bo8fxKJBU/hSJS2Kk/Y59UMGu/4Rs8yAnBNYRzSCyA==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-19.1.0.tgz", + "integrity": "sha512-dpPEmrSrY4EmnODcU9QdN4JydeEQF/k8C/2f+0Vu4OHPewaYDX48toby+OtJ9rp5ZVPv8+/XJEwOyX37m4nBng==", "dev": true, "dependencies": { - "@taquito/core": "^19.1.0-beta-RC.0", - "@taquito/http-utils": "^19.1.0-beta-RC.0", - "@taquito/utils": "^19.1.0-beta-RC.0", + "@taquito/core": "^19.1.0", + "@taquito/http-utils": "^19.1.0", + "@taquito/utils": "^19.1.0", "bignumber.js": "^9.1.2" }, "engines": { "node": ">=18" } }, - "node_modules/@taquito/rpc/node_modules/@taquito/utils": { - "version": "19.1.0-beta-RC.0", - "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-19.1.0-beta-RC.0.tgz", - "integrity": "sha512-7YmWeNVJ6Sh+Yy/Oyv/ch6ScAw8CuEoecnuJNusbkKn4eJI3xP4TEW5tqhnFrFT829Z2Wizi4kHoykz8W4I07A==", - "dev": true, - "dependencies": { - "@stablelib/blake2b": "^1.0.1", - "@stablelib/ed25519": "^1.0.3", - "@taquito/core": "^19.1.0-beta-RC.0", - "@types/bs58check": "^2.1.0", - "bignumber.js": "^9.1.2", - "blakejs": "^1.2.1", - "bs58check": "^3.0.1", - "buffer": "^6.0.3", - "elliptic": "^6.5.4", - "typedarray-to-buffer": "^4.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@taquito/signer": { - "version": "19.1.0-RC.1", - "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-19.1.0-RC.1.tgz", - "integrity": "sha512-rE1GRqf430aRC4e++aZ3yL7XHOmeldDbZyHQWwwNk/5k5wkF9v8GrjCKPEA2pan9/2e2IVCqBwHyuBcSSriCBQ==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-19.1.0.tgz", + "integrity": "sha512-JKaf+qh2NlJQG6184zvYYzWYnWIh1ffG1BV6PfS72aXM3963tOJ6YyRr5aPRrjrvIKHI+WPXIuXE4uZMZKTnIw==", "dev": true, "dependencies": { "@stablelib/blake2b": "^1.0.1", @@ -2105,9 +2042,9 @@ "@stablelib/nacl": "^1.0.4", "@stablelib/pbkdf2": "^1.0.1", "@stablelib/sha512": "^1.0.1", - "@taquito/core": "^19.1.0-RC.1", - "@taquito/taquito": "^19.1.0-RC.1", - "@taquito/utils": "^19.1.0-RC.1", + "@taquito/core": "^19.1.0", + "@taquito/taquito": "^19.1.0", + "@taquito/utils": "^19.1.0", "@types/bn.js": "^5.1.2", "bip39": "3.1.0", "elliptic": "^6.5.4", @@ -2119,19 +2056,19 @@ } }, "node_modules/@taquito/taquito": { - "version": "19.1.0-RC.1", - "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-19.1.0-RC.1.tgz", - "integrity": "sha512-yWYnwnLiyy9GYPARINWlbc2FSxmSVmkBQ89DZX21fnr3ZHcESnatstvI20qHy92FottlzKpRzZBSlhV2rmwbQw==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-19.1.0.tgz", + "integrity": "sha512-Rh3JXISzsx9E8gd4i4c2kFEspyNpIs6CgobkjuDLKHql4yUGRZg9q5pKWMSkdgrbdLwb8vtIvFZnPDsBJtCt/w==", "dev": true, "hasInstallScript": true, "dependencies": { - "@taquito/core": "^19.1.0-RC.1", - "@taquito/http-utils": "^19.1.0-RC.1", - "@taquito/local-forging": "^19.1.0-RC.1", - "@taquito/michel-codec": "^19.1.0-RC.1", - "@taquito/michelson-encoder": "^19.1.0-RC.1", - "@taquito/rpc": "^19.1.0-RC.1", - "@taquito/utils": "^19.1.0-RC.1", + "@taquito/core": "^19.1.0", + "@taquito/http-utils": "^19.1.0", + "@taquito/local-forging": "^19.1.0", + "@taquito/michel-codec": "^19.1.0", + "@taquito/michelson-encoder": "^19.1.0", + "@taquito/rpc": "^19.1.0", + "@taquito/utils": "^19.1.0", "bignumber.js": "^9.1.2", "rxjs": "^7.8.1" }, @@ -2140,13 +2077,13 @@ } }, "node_modules/@taquito/utils": { - "version": "19.1.0-RC.1", - "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-19.1.0-RC.1.tgz", - "integrity": "sha512-/qh/ary4XERxpWe+Or7XABFjJar/gdYaxf3a2c2vwM2MV3vQmPAaC593jVqyh7aDi7rmPFcj7NZJlbTVHQ4xZw==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-19.1.0.tgz", + "integrity": "sha512-vgEUWBb1n1PY8XpFqLJ2Axdlo/lNoIqNTeAyVHN7yxxqyVghSoMeWVsfbCJ661GJ32R1NNo4XAJrzwMR+B7soA==", "dependencies": { "@stablelib/blake2b": "^1.0.1", "@stablelib/ed25519": "^1.0.3", - "@taquito/core": "^19.1.0-RC.1", + "@taquito/core": "^19.1.0", "@types/bs58check": "^2.1.0", "bignumber.js": "^9.1.2", "blakejs": "^1.2.1", diff --git a/package.json b/package.json index 6eb6eb1..ec9330f 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,12 @@ "default": "./dist/node/index.js" }, "dependencies": { - "@taquito/utils": "^19.1.0-RC.1", + "@taquito/utils": "^19.1.0", "web3-utils": "^4.2.0", "web3-validator": "^2.0.4" }, "peerDependencies": { - "@taquito/taquito": "^19.1.0-RC.1", + "@taquito/taquito": ">=19.0.0", "web3": "^4.4.0", "ws": "^8.16.0" }, @@ -51,8 +51,8 @@ } }, "devDependencies": { - "@taquito/signer": "^19.1.0-RC.1", - "@taquito/taquito": "^19.1.0-RC.1", + "@taquito/signer": "^19.1.0", + "@taquito/taquito": "^19.1.0", "@types/jest": "^29.5.11", "@typescript-eslint/eslint-plugin": "^6.19.1", "@typescript-eslint/parser": "^6.19.1", diff --git a/src/bridgeBlockchainService/bridgeBlockchainService.ts b/src/bridgeBlockchainService/bridgeBlockchainService.ts new file mode 100644 index 0000000..965c203 --- /dev/null +++ b/src/bridgeBlockchainService/bridgeBlockchainService.ts @@ -0,0 +1,3 @@ +export interface BridgeBlockchainService { + getSignerAddress(): Promise; +} diff --git a/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/abi/index.ts b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/abi/index.ts new file mode 100644 index 0000000..9934d25 --- /dev/null +++ b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/abi/index.ts @@ -0,0 +1 @@ +export { kernelContractAbi } from './kernelContractAbi'; diff --git a/src/etherlink/contracts/kernelContract.ts b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/abi/kernelContractAbi.ts similarity index 97% rename from src/etherlink/contracts/kernelContract.ts rename to src/bridgeBlockchainService/etherlinkBridgeBlockchainService/abi/kernelContractAbi.ts index cfd8b6c..a8f443f 100644 --- a/src/etherlink/contracts/kernelContract.ts +++ b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/abi/kernelContractAbi.ts @@ -1,5 +1,3 @@ -import type { Contract } from 'web3'; - export const kernelContractAbi = [ { anonymous: false, @@ -189,5 +187,3 @@ export const kernelContractAbi = [ type: 'function' } ] as const; - -export type KernelContract = Contract; diff --git a/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/defaultAddresses.ts b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/defaultAddresses.ts new file mode 100644 index 0000000..014c0da --- /dev/null +++ b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/defaultAddresses.ts @@ -0,0 +1,5 @@ +export const defaultAddresses = { + etherlinkKernelAddress: '0x0000000000000000000000000000000000000000', + withdrawNativeTokenPrecompileAddress: '0x0000000000000000000000000000000000000020', + withdrawNonNativeTokenPrecompileAddress: '0x0000000000000000000000000000000000000040' +} as const; diff --git a/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/etherlinkBridgeBlockchainService.ts b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/etherlinkBridgeBlockchainService.ts new file mode 100644 index 0000000..61e71ba --- /dev/null +++ b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/etherlinkBridgeBlockchainService.ts @@ -0,0 +1,48 @@ +import type { NonNativeEtherlinkToken } from '../../tokens'; +import type { BridgeBlockchainService } from '../bridgeBlockchainService'; + +interface WithdrawTokenParamsBase { + amount: bigint; + tezosReceiverAddress: string; +} + +export interface WithdrawNativeTokenParams extends WithdrawTokenParamsBase { +} + +export interface WithdrawNonNativeTokenParams extends WithdrawTokenParamsBase { + token: NonNativeEtherlinkToken; + tezosTicketerAddress: string; + tezosTicketerContent: string; +} + +export interface CreateWithdrawNativeTokenOperationParams extends WithdrawNativeTokenParams { +} + +export interface CreateWithdrawNonNativeTokenOperationParams extends WithdrawNonNativeTokenParams { +} + +interface OperationResult { + hash: string; + timestamp: string; +} + +export interface WithdrawNativeTokenResult extends OperationResult { + amount: bigint; +} + +export interface WithdrawNonNativeTokenResult extends OperationResult { + amount: bigint; +} + +export interface EtherlinkBridgeBlockchainService< + TWithdrawNativeTokenExtraResult = unknown, + TWithdrawNonNativeTokenExtraResult = unknown, + TCreateWithdrawNativeTokenOperationResult = unknown, + TCreateWithdrawNonNativeTokenOperationResult = unknown, +> extends BridgeBlockchainService { + withdrawNativeToken(params: WithdrawNativeTokenParams): Promise; + withdrawNonNativeToken(params: WithdrawNonNativeTokenParams): Promise; + + createDepositNativeTokenOperation(params: CreateWithdrawNativeTokenOperationParams): Promise; + createDepositNonNativeTokenOperation(params: CreateWithdrawNonNativeTokenOperationParams): Promise; +} diff --git a/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/index.ts b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/index.ts new file mode 100644 index 0000000..4e27af3 --- /dev/null +++ b/src/bridgeBlockchainService/etherlinkBridgeBlockchainService/index.ts @@ -0,0 +1,3 @@ +export * from './abi'; +export * from './defaultAddresses'; +export type * from './etherlinkBridgeBlockchainService'; diff --git a/src/bridgeBlockchainService/index.ts b/src/bridgeBlockchainService/index.ts new file mode 100644 index 0000000..c83ffce --- /dev/null +++ b/src/bridgeBlockchainService/index.ts @@ -0,0 +1,32 @@ +export type { BridgeBlockchainService } from './bridgeBlockchainService'; + +export type { + TezosBridgeBlockchainService, + DepositNativeTokenParams, + DepositNonNativeTokensParams, + FinishWithdrawParams, + CreateDepositNativeTokenOperationParams, + CreateDepositNonNativeTokenOperationParams +} from './tezosBridgeBlockchainService'; + +export type { + EtherlinkBridgeBlockchainService, + WithdrawNativeTokenParams, + WithdrawNonNativeTokenParams, + CreateWithdrawNativeTokenOperationParams, + CreateWithdrawNonNativeTokenOperationParams +} from './etherlinkBridgeBlockchainService'; + +export { + TaquitoContractTezosBridgeBlockchainService, + TaquitoWalletTezosBridgeBlockchainService, + + type TaquitoContractTezosBridgeBlockchainServiceOptions, + type TaquitoWalletTezosBridgeBlockchainServiceOptions +} from './taquitoTezosBridgeBlockchainService'; + +export { + Web3EtherlinkBridgeBlockchainService, + + type Web3EtherlinkBridgeBlockchainServiceOptions +} from './web3EtherlinkBridgeBlockchainService'; diff --git a/src/tezos/contracts/fa12.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/fa12.ts similarity index 100% rename from src/tezos/contracts/fa12.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/fa12.ts diff --git a/src/tezos/contracts/fa2.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/fa2.ts similarity index 100% rename from src/tezos/contracts/fa2.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/fa2.ts diff --git a/src/tezos/contracts/index.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/index.ts similarity index 100% rename from src/tezos/contracts/index.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/index.ts diff --git a/src/tezos/contracts/nativeTokenTicketHelper.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/nativeTokenTicketHelper.ts similarity index 100% rename from src/tezos/contracts/nativeTokenTicketHelper.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/nativeTokenTicketHelper.ts diff --git a/src/tezos/contracts/nonNativeTokenTicketHelper.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/nonNativeTokenTicketHelper.ts similarity index 100% rename from src/tezos/contracts/nonNativeTokenTicketHelper.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/contracts/nonNativeTokenTicketHelper.ts diff --git a/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/errors.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/errors.ts new file mode 100644 index 0000000..b3dd985 --- /dev/null +++ b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/errors.ts @@ -0,0 +1,7 @@ +import { TokenBridgeError } from '../../common'; + +export class TezosSignerAccountUnavailableError extends TokenBridgeError { + constructor() { + super('The Tezos signer account is unavailable'); + } +} diff --git a/src/tezos/helpers/fa12helper.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/helpers/fa12helper.ts similarity index 100% rename from src/tezos/helpers/fa12helper.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/helpers/fa12helper.ts diff --git a/src/tezos/helpers/fa2helper.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/helpers/fa2helper.ts similarity index 100% rename from src/tezos/helpers/fa2helper.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/helpers/fa2helper.ts diff --git a/src/tezos/helpers/index.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/helpers/index.ts similarity index 100% rename from src/tezos/helpers/index.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/helpers/index.ts diff --git a/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/index.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/index.ts new file mode 100644 index 0000000..dfb4029 --- /dev/null +++ b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/index.ts @@ -0,0 +1,10 @@ +export { + TaquitoContractTezosBridgeBlockchainService, + type TaquitoContractTezosBridgeBlockchainServiceOptions +} from './taquitoContractTezosBridgeBlockchainService'; + +export { + TaquitoWalletTezosBridgeBlockchainService, + type TaquitoWalletTezosBridgeBlockchainServiceOptions +} from './taquitoWalletTezosBridgeBlockchainService'; + diff --git a/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoContractTezosBridgeBlockchainService.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoContractTezosBridgeBlockchainService.ts new file mode 100644 index 0000000..16cc597 --- /dev/null +++ b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoContractTezosBridgeBlockchainService.ts @@ -0,0 +1,66 @@ +import type { BatchOperation, ContractProvider, OperationBatch, ParamsWithKind, TransactionOperation } from '@taquito/taquito'; + +import type { FA12Contract, FA2Contract, NativeTokenTicketHelper, NonNativeTokenTicketHelper } from './contracts'; +import { TaquitoTezosBridgeBlockchainService, type TaquitoTezosBridgeBlockchainServiceOptions } from './taquitoTezosBridgeBlockchainService'; +import type { + DepositNativeTokenParams, DepositNativeTokenResult, + DepositNonNativeTokensParams, DepositNonNativeTokenResult, + FinishWithdrawParams, FinishWithdrawResult +} from '../tezosBridgeBlockchainService'; + +export interface TaquitoContractTezosBridgeBlockchainServiceOptions extends TaquitoTezosBridgeBlockchainServiceOptions { +} + +export class TaquitoContractTezosBridgeBlockchainService extends TaquitoTezosBridgeBlockchainService { + async depositNativeToken(params: DepositNativeTokenParams): Promise { + const operation = await this.depositNativeTokenInternal(params); + + return { + amount: params.amount, + hash: operation.hash, + timestamp: this.getCurrentOperationTimestamp(), + operation + }; + } + + async depositNonNativeToken(params: DepositNonNativeTokensParams): Promise { + const operation = await this.depositNonNativeTokenInternal(params); + + return { + amount: params.amount, + hash: operation.hash, + timestamp: this.getCurrentOperationTimestamp(), + operation + }; + } + + async finishWithdraw(params: FinishWithdrawParams): Promise>; }> { + const operation = await this.finishWithdrawInternal(params); + + return { + hash: operation.hash, + timestamp: this.getCurrentOperationTimestamp(), + operation + }; + } + + protected createBatch(params?: ParamsWithKind[]): OperationBatch { + return this.tezosToolkit.contract.batch(params); + } + + protected getNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise> { + return this.tezosToolkit.contract.at>(ticketHelperContractAddress); + } + + protected getNonNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise> { + return this.tezosToolkit.contract.at>(ticketHelperContractAddress); + } + + protected getFA12TokenContract(fa12TokenContractAddress: string): Promise> { + return this.tezosToolkit.contract.at>(fa12TokenContractAddress); + } + + protected getFA2TokenContract(fa2TokenContractAddress: string): Promise> { + return this.tezosToolkit.contract.at>(fa2TokenContractAddress); + } +} diff --git a/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoTezosBridgeBlockchainService.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoTezosBridgeBlockchainService.ts new file mode 100644 index 0000000..1288b08 --- /dev/null +++ b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoTezosBridgeBlockchainService.ts @@ -0,0 +1,203 @@ +import { packDataBytes } from '@taquito/michel-codec'; +import { Schema } from '@taquito/michelson-encoder'; +import type { TezosToolkit, Wallet, ContractProvider, ContractMethod } from '@taquito/taquito'; + +import type { FA12Contract, FA2Contract, NativeTokenTicketHelper, NonNativeTokenTicketHelper } from './contracts'; +import { TezosSignerAccountUnavailableError } from './errors'; +import { fa12helper, fa2helper } from './helpers'; +import { tezosTicketerContentMichelsonType } from './tezosTicketerContentMichelsonType'; +import { getErrorLogMessage, loggerProvider } from '../../logging'; +import type { FA12TezosToken, FA2TezosToken } from '../../tokens'; +import { etherlinkUtils } from '../../utils'; +import type { + TezosBridgeBlockchainService, + DepositNativeTokenParams, DepositNonNativeTokensParams, FinishWithdrawParams, + DepositNativeTokenResult, DepositNonNativeTokenResult, FinishWithdrawResult, + CreateDepositNativeTokenOperationParams, CreateDepositNonNativeTokenOperationParams, +} from '../tezosBridgeBlockchainService'; + +export interface TaquitoTezosBridgeBlockchainServiceOptions { + tezosToolkit: TezosToolkit; + smartRollupAddress: string; +} + +export abstract class TaquitoTezosBridgeBlockchainService< + TApi extends ContractProvider | Wallet +> implements TezosBridgeBlockchainService< + DepositNativeTokenResult & { operation: Awaited['send']>> }, + DepositNonNativeTokenResult & { operation: Awaited['send']>> }, + FinishWithdrawResult & { operation: Awaited> }, + ContractMethod, + ContractMethod +> { + readonly smartRollupAddress: string; + + protected readonly tezosToolkit: TezosToolkit; + protected readonly tezosTicketerContentSchema = new Schema(tezosTicketerContentMichelsonType); + + constructor(options: TaquitoTezosBridgeBlockchainServiceOptions) { + this.smartRollupAddress = options.smartRollupAddress; + this.tezosToolkit = options.tezosToolkit; + } + + async getSignerAddress(): Promise { + try { + return await this.tezosToolkit.signer.publicKeyHash(); + } + catch (error) { + loggerProvider.logger.error(getErrorLogMessage(error)); + + return undefined; + } + } + + abstract depositNativeToken( + params: DepositNativeTokenParams + ): Promise['send']>>; }>; + + abstract depositNonNativeToken( + params: DepositNonNativeTokensParams + ): Promise['send']>>; }>; + + abstract finishWithdraw( + params: FinishWithdrawParams + ): Promise>; }>; + + async createDepositNativeTokenOperation(params: CreateDepositNativeTokenOperationParams) { + const ticketHelperContract = await this.getNativeTokenTicketHelperContract(params.ticketHelperContractAddress); + const routingInfo = this.packDepositRoutingInfo(params.etherlinkReceiverAddress); + + const operation = ticketHelperContract.methodsObject.deposit({ + evm_address: this.smartRollupAddress, + l2_address: routingInfo, + }); + + return operation; + } + + async createDepositNonNativeTokenOperation(params: CreateDepositNonNativeTokenOperationParams) { + const ticketHelperContract = await this.getNonNativeTokenTicketHelperContract(params.ticketHelperContractAddress); + const routingInfo = this.packDepositRoutingInfo(params.etherlinkReceiverAddress); + + const operation = ticketHelperContract.methodsObject.deposit({ + rollup: this.smartRollupAddress, + receiver: routingInfo, + amount: params.amount + }); + + return operation; + } + + async getTezosTicketerContent(tezosTicketerAddress: string): Promise { + const storage = await this.tezosToolkit.contract.getStorage<{ content: any }>(tezosTicketerAddress); + const content = [...Object.values(storage.content)]; + const contentMichelsonData = this.tezosTicketerContentSchema.Encode(content); + + return '0x' + packDataBytes(contentMichelsonData, tezosTicketerContentMichelsonType).bytes.slice(2); + } + + protected async depositNativeTokenInternal(params: DepositNativeTokenParams) { + const depositOperation = await this.createDepositNativeTokenOperation(params); + + return depositOperation.send({ + amount: Number(params.amount), + mutez: true + }) as Promise['send']>>>; + } + + protected async depositNonNativeTokenInternal(params: DepositNonNativeTokensParams): Promise['send']>>> { + const useApprove = params.useApprove ?? true; + const depositOperation = await this.createDepositNonNativeTokenOperation(params); + + let batch = this.createBatch(); + + if (useApprove) { + batch = await (params.token.type === 'fa2' + ? this.wrapContractCallsWithFA2TokenApprove( + batch, + depositOperation, + params.token, + params.ticketHelperContractAddress + ) + : this.wrapContractCallsWithFA12TokenApprove( + batch, + depositOperation, + params.amount, + params.token, + params.ticketHelperContractAddress, + params.resetFA12Approve ?? true + )); + } + + return batch.send() as Promise['send']>>>; + } + + protected finishWithdrawInternal(params: FinishWithdrawParams) { + return this.tezosToolkit.contract.smartRollupExecuteOutboxMessage({ + rollup: this.smartRollupAddress, + cementedCommitment: params.cementedCommitment, + outputProof: params.outputProof + }); + } + + protected getCurrentOperationTimestamp() { + return new Date().toISOString(); + } + + protected async wrapContractCallsWithFA12TokenApprove( + batch: ReturnType, + contractCalls: Parameters[0]['contractCalls'], + amount: bigint, + token: FA12TezosToken, + ticketHelperContractAddress: string, + isNeedToReset: boolean + ): Promise> { + const tokenContract = await this.getFA12TokenContract(token.address); + const resultOperation = fa12helper.wrapContractCallsWithApprove({ + batch, + approvedAddress: ticketHelperContractAddress, + approvedAmount: amount, + tokenContract, + contractCalls, + isNeedToReset + }); + + return resultOperation as ReturnType; + } + + protected async wrapContractCallsWithFA2TokenApprove( + batch: ReturnType, + contractCalls: Parameters[0]['contractCalls'], + token: FA2TezosToken, + ticketHelperContractAddress: string + ): Promise> { + const [tokenContract, tokenOwnerAddress] = await Promise.all([ + this.getFA2TokenContract(token.address), + this.getSignerAddress() + ]); + + if (!tokenOwnerAddress) + throw new TezosSignerAccountUnavailableError(); + + const resultOperation = fa2helper.wrapContractCallsWithApprove({ + batch, + approvedAddress: ticketHelperContractAddress, + ownerAddress: tokenOwnerAddress, + tokenId: token.tokenId, + tokenContract, + contractCalls + }); + + return resultOperation as ReturnType; + } + + protected packDepositRoutingInfo(etherlinkReceiverAddress: string): string { + return etherlinkUtils.prepareHexPrefix(etherlinkReceiverAddress, false); + } + + protected abstract createBatch(params?: Parameters[0]): ReturnType; + protected abstract getNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise>; + protected abstract getNonNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise>; + protected abstract getFA12TokenContract(fa12TokenContractAddress: string): Promise>; + protected abstract getFA2TokenContract(fa2TokenContractAddress: string): Promise>; +} diff --git a/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoWalletTezosBridgeBlockchainService.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoWalletTezosBridgeBlockchainService.ts new file mode 100644 index 0000000..41ddadf --- /dev/null +++ b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoWalletTezosBridgeBlockchainService.ts @@ -0,0 +1,66 @@ +import type { ContractProvider, TransactionWalletOperation, Wallet, WalletOperationBatch, WalletParamsWithKind } from '@taquito/taquito'; + +import type { FA12Contract, FA2Contract, NativeTokenTicketHelper, NonNativeTokenTicketHelper } from './contracts'; +import { TaquitoTezosBridgeBlockchainService, type TaquitoTezosBridgeBlockchainServiceOptions } from './taquitoTezosBridgeBlockchainService'; +import type { + DepositNativeTokenParams, DepositNativeTokenResult, + DepositNonNativeTokenResult, DepositNonNativeTokensParams, + FinishWithdrawParams, FinishWithdrawResult +} from '../tezosBridgeBlockchainService'; + +export interface TaquitoWalletTezosBridgeBlockchainServiceOptions extends TaquitoTezosBridgeBlockchainServiceOptions { +} + +export class TaquitoWalletTezosBridgeBlockchainService extends TaquitoTezosBridgeBlockchainService { + async depositNativeToken(params: DepositNativeTokenParams): Promise { + const operation = await this.depositNativeTokenInternal(params); + + return { + amount: params.amount, + hash: operation.opHash, + timestamp: this.getCurrentOperationTimestamp(), + operation + }; + } + + async depositNonNativeToken(params: DepositNonNativeTokensParams): Promise['send']>>; }> { + const operation = await this.depositNonNativeTokenInternal(params); + + return { + amount: params.amount, + hash: operation.opHash, + timestamp: this.getCurrentOperationTimestamp(), + operation + }; + } + + async finishWithdraw(params: FinishWithdrawParams): Promise>; }> { + const operation = await this.finishWithdrawInternal(params); + + return { + hash: operation.hash, + timestamp: this.getCurrentOperationTimestamp(), + operation + }; + } + + protected createBatch(params?: WalletParamsWithKind[]): WalletOperationBatch { + return this.tezosToolkit.wallet.batch(params); + } + + protected getNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise> { + return this.tezosToolkit.wallet.at>(ticketHelperContractAddress); + } + + protected getNonNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise> { + return this.tezosToolkit.wallet.at>(ticketHelperContractAddress); + } + + protected getFA12TokenContract(fa12TokenContractAddress: string): Promise> { + return this.tezosToolkit.wallet.at>(fa12TokenContractAddress); + } + + protected getFA2TokenContract(fa2TokenContractAddress: string): Promise> { + return this.tezosToolkit.wallet.at>(fa2TokenContractAddress); + } +} diff --git a/src/tezos/tezosTicketerContentMichelsonType.ts b/src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/tezosTicketerContentMichelsonType.ts similarity index 100% rename from src/tezos/tezosTicketerContentMichelsonType.ts rename to src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/tezosTicketerContentMichelsonType.ts diff --git a/src/bridgeBlockchainService/tezosBridgeBlockchainService/index.ts b/src/bridgeBlockchainService/tezosBridgeBlockchainService/index.ts new file mode 100644 index 0000000..aff66ab --- /dev/null +++ b/src/bridgeBlockchainService/tezosBridgeBlockchainService/index.ts @@ -0,0 +1 @@ +export type * from './tezosBridgeBlockchainService'; diff --git a/src/bridgeBlockchainService/tezosBridgeBlockchainService/tezosBridgeBlockchainService.ts b/src/bridgeBlockchainService/tezosBridgeBlockchainService/tezosBridgeBlockchainService.ts new file mode 100644 index 0000000..40d512b --- /dev/null +++ b/src/bridgeBlockchainService/tezosBridgeBlockchainService/tezosBridgeBlockchainService.ts @@ -0,0 +1,64 @@ +import type { NonNativeTezosToken } from '../../tokens'; +import type { BridgeBlockchainService } from '../bridgeBlockchainService'; + +interface DepositTokenParamsBase { + amount: bigint; + etherlinkReceiverAddress: string; + ticketHelperContractAddress: string; +} + +export interface DepositNativeTokenParams extends DepositTokenParamsBase { +} + +export interface DepositNonNativeTokensParams extends DepositTokenParamsBase { + token: NonNativeTezosToken; + useApprove?: boolean; + resetFA12Approve?: boolean; +} + +export interface FinishWithdrawParams { + cementedCommitment: string; + outputProof: string; +} + +export interface CreateDepositNativeTokenOperationParams extends DepositNativeTokenParams { +} + +export interface CreateDepositNonNativeTokenOperationParams extends CreateDepositNativeTokenOperationParams { +} + +interface OperationResult { + hash: string; + timestamp: string; +} + +export interface DepositNativeTokenResult extends OperationResult { + amount: bigint; +} + +export interface DepositNonNativeTokenResult extends OperationResult { + amount: bigint; +} + +export interface FinishWithdrawResult extends OperationResult { +} + +export interface TezosBridgeBlockchainService< + TDepositNativeTokenExtraResult = unknown, + TDepositNonNativeTokenExtraResult = unknown, + TFinishWithdrawExtraResult = unknown, + TCreateDepositNativeTokenOperationResult = unknown, + TCreateDepositNonNativeTokenOperationResult = unknown, +> extends BridgeBlockchainService { + readonly smartRollupAddress: string; + + depositNativeToken(params: DepositNativeTokenParams): Promise; + depositNonNativeToken(params: DepositNonNativeTokensParams): Promise; + + finishWithdraw(params: FinishWithdrawParams): Promise; + + createDepositNativeTokenOperation(params: CreateDepositNativeTokenOperationParams): Promise; + createDepositNonNativeTokenOperation(params: CreateDepositNonNativeTokenOperationParams): Promise; + + getTezosTicketerContent(tezosTicketerAddress: string): Promise; +} diff --git a/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/index.ts b/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/index.ts new file mode 100644 index 0000000..ce4f143 --- /dev/null +++ b/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/index.ts @@ -0,0 +1 @@ +export { Web3EtherlinkBridgeBlockchainService, type Web3EtherlinkBridgeBlockchainServiceOptions } from './web3EtherlinkBridgeBlockchainService'; diff --git a/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/precompiles.ts b/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/precompiles.ts new file mode 100644 index 0000000..f41ed3c --- /dev/null +++ b/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/precompiles.ts @@ -0,0 +1,5 @@ +import type { Contract } from 'web3'; + +import { kernelContractAbi } from '../etherlinkBridgeBlockchainService'; + +export type WithdrawNonNativeTokenPrecompile = Contract; diff --git a/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/web3EtherlinkBridgeBlockchainService.ts b/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/web3EtherlinkBridgeBlockchainService.ts new file mode 100644 index 0000000..6d9af93 --- /dev/null +++ b/src/bridgeBlockchainService/web3EtherlinkBridgeBlockchainService/web3EtherlinkBridgeBlockchainService.ts @@ -0,0 +1,110 @@ +import type { Web3, TransactionReceipt } from 'web3'; +import type { NonPayableMethodObject } from 'web3-eth-contract'; + +import type { WithdrawNonNativeTokenPrecompile } from './precompiles'; +import { getErrorLogMessage, loggerProvider } from '../../logging'; +import { tezosUtils } from '../../utils'; +import { + kernelContractAbi, + defaultAddresses, + type EtherlinkBridgeBlockchainService, + type WithdrawNativeTokenParams, + type WithdrawNativeTokenResult, + type WithdrawNonNativeTokenParams, + type WithdrawNonNativeTokenResult, + type CreateWithdrawNativeTokenOperationParams, + type CreateWithdrawNonNativeTokenOperationParams, +} from '../etherlinkBridgeBlockchainService'; + +export interface Web3EtherlinkBridgeBlockchainServiceOptions { + web3: Web3; + kernelAddress?: string; + withdrawNativeTokenPrecompileAddress?: string; + withdrawNonNativeTokenPrecompileAddress?: string; +} + +export class Web3EtherlinkBridgeBlockchainService implements EtherlinkBridgeBlockchainService< + { receipt: TransactionReceipt }, + { receipt: TransactionReceipt }, + NonPayableMethodObject, + NonPayableMethodObject +> { + protected readonly web3: Web3; + protected readonly withdrawNativeTokenPrecompiledAddress: string; + protected readonly withdrawNonNativeTokenPrecompiledAddress: string; + protected readonly withdrawNonNativeTokenPrecompile: WithdrawNonNativeTokenPrecompile; + + constructor(options: Web3EtherlinkBridgeBlockchainServiceOptions) { + this.web3 = options.web3; + this.withdrawNativeTokenPrecompiledAddress = options.withdrawNativeTokenPrecompileAddress || defaultAddresses.withdrawNativeTokenPrecompileAddress; + this.withdrawNonNativeTokenPrecompiledAddress = options.withdrawNonNativeTokenPrecompileAddress || defaultAddresses.withdrawNonNativeTokenPrecompileAddress; + + this.withdrawNonNativeTokenPrecompile = new this.web3.eth.Contract( + kernelContractAbi, + this.withdrawNonNativeTokenPrecompiledAddress + ); + } + + async getSignerAddress(): Promise { + try { + const accounts = await this.web3.eth.getAccounts(); + return accounts[0] || this.web3.eth.defaultAccount; + } + catch (error) { + loggerProvider.logger.error(getErrorLogMessage(error)); + return undefined; + } + } + + withdrawNativeToken(_params: WithdrawNativeTokenParams): Promise { + throw new Error('Withdrawal of native tokens is not supported yet'); + } + + async withdrawNonNativeToken(params: WithdrawNonNativeTokenParams): Promise { + const [nonNativeTokenOperation, gasPrice, signerAddress] = await Promise.all([ + this.createDepositNonNativeTokenOperation(params), + this.web3.eth.getGasPrice(), + this.getSignerAddress() + ]); + const data = nonNativeTokenOperation.encodeABI(); + // TODO: need to calculate the value or hardcode it in config + const receipt = await this.web3.eth.sendTransaction({ + from: signerAddress, + to: this.withdrawNonNativeTokenPrecompiledAddress, + gas: 30000n, + gasPrice, + data, + }); + + return { + hash: receipt.transactionHash.toString(), + amount: params.amount, + timestamp: this.getCurrentTransactionTimestamp(), + receipt + }; + } + + createDepositNativeTokenOperation(_params: CreateWithdrawNativeTokenOperationParams): Promise> { + throw new Error('Withdrawal of native tokens is not supported yet'); + } + + createDepositNonNativeTokenOperation(params: CreateWithdrawNonNativeTokenOperationParams): Promise> { + const tezosReceiverAddressBytes = tezosUtils.convertAddressToBytes(params.tezosReceiverAddress, true); + const tezosTicketerAddressBytes = tezosUtils.convertAddressToBytes(params.tezosTicketerAddress, true); + const tezosProxyAddressBytes = tezosUtils.convertAddressToBytes(params.tezosTicketerAddress); + const receiverBytes = tezosReceiverAddressBytes + tezosProxyAddressBytes; + + return Promise.resolve(this.withdrawNonNativeTokenPrecompile.methods + .withdraw( + params.token.address, + receiverBytes, + params.amount, + tezosTicketerAddressBytes, + params.tezosTicketerContent + )); + } + + protected getCurrentTransactionTimestamp() { + return new Date().toISOString(); + } +} diff --git a/src/bridgeCore/bridgeOperations.ts b/src/bridgeCore/bridgeOperations.ts index 4c4b805..05697fe 100644 --- a/src/bridgeCore/bridgeOperations.ts +++ b/src/bridgeCore/bridgeOperations.ts @@ -1,30 +1,33 @@ -import type { EtherlinkToken } from '../etherlink'; -import type { TezosToken } from '../tezos'; +import type { TezosToken, EtherlinkToken } from '../tokens'; export interface TezosTransferTokensOperation { readonly blockId: number; readonly hash: string; + readonly counter: number; + readonly nonce: number | null; readonly amount: bigint; readonly token: TezosToken; - readonly fee: bigint; readonly timestamp: string; } export interface EtherlinkTransferTokensOperation { readonly blockId: number; readonly hash: string; + readonly logIndex: number; readonly amount: bigint; readonly token: EtherlinkToken; - readonly fee: bigint; readonly timestamp: string; } export interface InitialRollupData { readonly outboxMessageLevel: number; readonly outboxMessageIndex: number; + readonly estimatedOutboxMessageExecutionTimestamp?: string; } -export interface CementedRollupData extends InitialRollupData { +export interface CementedRollupData { + readonly outboxMessageLevel: number; + readonly outboxMessageIndex: number; readonly commitment: string; readonly proof: string; } @@ -67,22 +70,33 @@ export interface PendingBridgeTokenDeposit extends BridgeTokenTransferBase { } export interface CreatedBridgeTokenDeposit extends BridgeTokenTransferBase { + readonly id: string; readonly kind: BridgeTokenTransferKind.Deposit; readonly status: BridgeTokenTransferStatus.Created; readonly tezosOperation: TezosTransferTokensOperation; } export interface FinishedBridgeTokenDeposit extends BridgeTokenTransferBase { + readonly id: string; readonly kind: BridgeTokenTransferKind.Deposit; readonly status: BridgeTokenTransferStatus.Finished; readonly tezosOperation: TezosTransferTokensOperation; readonly etherlinkOperation: EtherlinkTransferTokensOperation; } +export interface FailedBridgeTokenDeposit extends BridgeTokenTransferBase { + readonly id: string; + readonly kind: BridgeTokenTransferKind.Deposit; + readonly status: BridgeTokenTransferStatus.Failed; + readonly tezosOperation: TezosTransferTokensOperation; + readonly etherlinkOperation?: EtherlinkTransferTokensOperation; +} + export type BridgeTokenDeposit = | PendingBridgeTokenDeposit | CreatedBridgeTokenDeposit - | FinishedBridgeTokenDeposit; + | FinishedBridgeTokenDeposit + | FailedBridgeTokenDeposit; export interface PendingBridgeTokenWithdrawal extends BridgeTokenTransferBase { readonly kind: BridgeTokenTransferKind.Withdrawal; @@ -96,6 +110,7 @@ export interface PendingBridgeTokenWithdrawal extends BridgeTokenTransferBase { } export interface CreatedBridgeTokenWithdrawal extends BridgeTokenTransferBase { + readonly id: string; readonly kind: BridgeTokenTransferKind.Withdrawal; readonly status: BridgeTokenTransferStatus.Created; readonly etherlinkOperation: EtherlinkTransferTokensOperation; @@ -103,6 +118,7 @@ export interface CreatedBridgeTokenWithdrawal extends BridgeTokenTransferBase { } export interface SealedBridgeTokenWithdrawal extends BridgeTokenTransferBase { + readonly id: string; readonly kind: BridgeTokenTransferKind.Withdrawal; readonly status: BridgeTokenTransferStatus.Sealed; readonly etherlinkOperation: EtherlinkTransferTokensOperation; @@ -110,18 +126,29 @@ export interface SealedBridgeTokenWithdrawal extends BridgeTokenTransferBase { } export interface FinishedBridgeTokenWithdrawal extends BridgeTokenTransferBase { + readonly id: string; readonly kind: BridgeTokenTransferKind.Withdrawal; readonly status: BridgeTokenTransferStatus.Finished; - readonly tezosOperation: TezosTransferTokensOperation; readonly etherlinkOperation: EtherlinkTransferTokensOperation; readonly rollupData: CementedRollupData; + readonly tezosOperation: TezosTransferTokensOperation; +} + +export interface FailedBridgeTokenWithdrawal extends BridgeTokenTransferBase { + readonly id: string; + readonly kind: BridgeTokenTransferKind.Withdrawal; + readonly status: BridgeTokenTransferStatus.Failed; + readonly etherlinkOperation: EtherlinkTransferTokensOperation; + readonly rollupData?: Partial; + readonly tezosOperation?: TezosTransferTokensOperation; } export type BridgeTokenWithdrawal = | PendingBridgeTokenWithdrawal | CreatedBridgeTokenWithdrawal | SealedBridgeTokenWithdrawal - | FinishedBridgeTokenWithdrawal; + | FinishedBridgeTokenWithdrawal + | FailedBridgeTokenWithdrawal; export type BridgeTokenTransfer = | BridgeTokenDeposit diff --git a/src/bridgeCore/depositOptions.ts b/src/bridgeCore/depositOptions.ts index 33a882e..ecacaf9 100644 --- a/src/bridgeCore/depositOptions.ts +++ b/src/bridgeCore/depositOptions.ts @@ -1,12 +1,4 @@ -interface DepositOptionsBase { +export interface DepositOptions { useApprove?: boolean; resetFA12Approve?: boolean; } - -export interface DepositOptions extends DepositOptionsBase { - useWalletApi: false; -} - -export interface WalletDepositOptions extends DepositOptionsBase { - useWalletApi?: true; -} diff --git a/src/bridgeCore/depositResult.ts b/src/bridgeCore/depositResult.ts deleted file mode 100644 index 2ec5934..0000000 --- a/src/bridgeCore/depositResult.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { ContractProvider, Wallet } from '@taquito/taquito'; - -import type { PendingBridgeTokenDeposit } from './bridgeOperations'; - -export interface WalletDepositResult { - tokenTransfer: PendingBridgeTokenDeposit; - depositOperation: Awaited['send']>>; -} - -export interface DepositResult { - tokenTransfer: PendingBridgeTokenDeposit; - depositOperation: Awaited['send']>>; -} diff --git a/src/bridgeCore/index.ts b/src/bridgeCore/index.ts index 692d87c..a26e166 100644 --- a/src/bridgeCore/index.ts +++ b/src/bridgeCore/index.ts @@ -4,31 +4,24 @@ export { BridgeTokenTransferKind, BridgeTokenTransferStatus, + type TezosTransferTokensOperation, + type EtherlinkTransferTokensOperation, + type BridgeTokenTransfer, type BridgeTokenDeposit, type PendingBridgeTokenDeposit, type CreatedBridgeTokenDeposit, type FinishedBridgeTokenDeposit, + type FailedBridgeTokenDeposit, type BridgeTokenWithdrawal, type PendingBridgeTokenWithdrawal, type CreatedBridgeTokenWithdrawal, type SealedBridgeTokenWithdrawal, - type FinishedBridgeTokenWithdrawal + type FinishedBridgeTokenWithdrawal, + type FailedBridgeTokenWithdrawal } from './bridgeOperations'; -export type { - DepositOptions, - WalletDepositOptions -} from './depositOptions'; - -export type { - DepositResult, - WalletDepositResult -} from './depositResult'; - -export type { - StartWithdrawResult, - FinishWithdrawResult -} from './withdrawResult'; +export type { DepositOptions } from './depositOptions'; +export type { DepositResult, StartWithdrawResult, FinishWithdrawResult } from './operationResults'; diff --git a/src/bridgeCore/operationResults.ts b/src/bridgeCore/operationResults.ts new file mode 100644 index 0000000..a4090a1 --- /dev/null +++ b/src/bridgeCore/operationResults.ts @@ -0,0 +1,16 @@ +import type { PendingBridgeTokenDeposit, PendingBridgeTokenWithdrawal, SealedBridgeTokenWithdrawal } from './bridgeOperations'; + +export interface StartWithdrawResult Promise> { + tokenTransfer: PendingBridgeTokenWithdrawal; + operationResult: Awaited>; +} + +export interface FinishWithdrawResult Promise> { + tokenTransfer: SealedBridgeTokenWithdrawal; + operationResult: Awaited>; +} + +export interface DepositResult Promise> { + tokenTransfer: PendingBridgeTokenDeposit; + operationResult: Awaited>; +} diff --git a/src/bridgeCore/tokenPair.ts b/src/bridgeCore/tokenPair.ts index 69f53dd..ac407ef 100644 --- a/src/bridgeCore/tokenPair.ts +++ b/src/bridgeCore/tokenPair.ts @@ -1,5 +1,7 @@ -import type { NativeEtherlinkToken, ERC20EtherlinkToken } from '../etherlink'; -import type { NativeTezosToken, FA12TezosToken, FA2TezosToken } from '../tezos'; +import type { + NativeTezosToken, FA12TezosToken, FA2TezosToken, + NativeEtherlinkToken, ERC20EtherlinkToken +} from '../tokens'; interface NativeTezosTokenTicketerInfo { readonly ticketHelperContractAddress: string; diff --git a/src/bridgeCore/withdrawResult.ts b/src/bridgeCore/withdrawResult.ts deleted file mode 100644 index f0a487f..0000000 --- a/src/bridgeCore/withdrawResult.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { ContractProvider } from '@taquito/taquito'; -import type { TransactionReceipt } from 'web3'; - -import type { PendingBridgeTokenWithdrawal, SealedBridgeTokenWithdrawal } from './bridgeOperations'; - -export interface StartWithdrawResult { - tokenTransfer: PendingBridgeTokenWithdrawal; - startWithdrawOperation: TransactionReceipt; -} - -export interface FinishWithdrawResult { - tokenTransfer: SealedBridgeTokenWithdrawal; - finishWithdrawOperation: Awaited>; -} diff --git a/src/bridgeDataProviders/balancesBridgeDataProvider/accountTokenBalances.ts b/src/bridgeDataProviders/balancesBridgeDataProvider/accountTokenBalances.ts index 795f693..9d75f8b 100644 --- a/src/bridgeDataProviders/balancesBridgeDataProvider/accountTokenBalances.ts +++ b/src/bridgeDataProviders/balancesBridgeDataProvider/accountTokenBalances.ts @@ -1,5 +1,4 @@ -import type { EtherlinkToken } from '../../etherlink'; -import type { TezosToken } from '../../tezos'; +import type { TezosToken, EtherlinkToken } from '../../tokens'; export interface TokenBalanceInfo { readonly token: TezosToken | EtherlinkToken; diff --git a/src/bridgeDataProviders/balancesBridgeDataProvider/balancesBridgeDataProvider.ts b/src/bridgeDataProviders/balancesBridgeDataProvider/balancesBridgeDataProvider.ts index ba3bf2f..0f5e16b 100644 --- a/src/bridgeDataProviders/balancesBridgeDataProvider/balancesBridgeDataProvider.ts +++ b/src/bridgeDataProviders/balancesBridgeDataProvider/balancesBridgeDataProvider.ts @@ -1,12 +1,12 @@ import type { AccountTokenBalance, AccountTokenBalances } from './accountTokenBalances'; -import type { EtherlinkToken } from '../../etherlink'; -import type { TezosToken } from '../../tezos'; +import type { BalancesFetchOptions } from './balancesFetchOptions'; +import type { TezosToken, EtherlinkToken } from '../../tokens'; export interface BalancesBridgeDataProvider { getBalance(accountAddress: string, token: TezosToken | EtherlinkToken): Promise; getBalances(accountAddress: string): Promise; getBalances(accountAddress: string, tokens: ReadonlyArray): Promise; - getBalances(accountAddress: string, offset: number, limit: number): Promise; - getBalances(accountAddress: string, tokensOrOffset?: ReadonlyArray | number, limit?: number): Promise; + getBalances(accountAddress: string, fetchOptions: BalancesFetchOptions): Promise; + getBalances(accountAddress: string, tokensOrFetchOptions?: ReadonlyArray | BalancesFetchOptions): Promise; } diff --git a/src/bridgeDataProviders/balancesBridgeDataProvider/balancesFetchOptions.ts b/src/bridgeDataProviders/balancesBridgeDataProvider/balancesFetchOptions.ts new file mode 100644 index 0000000..5daed29 --- /dev/null +++ b/src/bridgeDataProviders/balancesBridgeDataProvider/balancesFetchOptions.ts @@ -0,0 +1,4 @@ +import type { FetchOptions } from '../../common'; + +export interface BalancesFetchOptions extends FetchOptions { +} diff --git a/src/bridgeDataProviders/balancesBridgeDataProvider/etherlinkNodeBalancesProvider/error.ts b/src/bridgeDataProviders/balancesBridgeDataProvider/etherlinkNodeBalancesProvider/error.ts index 05a11cd..37c5071 100644 --- a/src/bridgeDataProviders/balancesBridgeDataProvider/etherlinkNodeBalancesProvider/error.ts +++ b/src/bridgeDataProviders/balancesBridgeDataProvider/etherlinkNodeBalancesProvider/error.ts @@ -1,6 +1,6 @@ import type { RPCNodeError } from './dto'; import { TokenBridgeError } from '../../../common'; -import type { NativeEtherlinkToken } from '../../../etherlink'; +import type { NativeEtherlinkToken } from '../../../tokens'; import { tokenUtils } from '../../../utils'; export class EtherlinkNodeTokenBalanceNotSupported extends TokenBridgeError { diff --git a/src/bridgeDataProviders/balancesBridgeDataProvider/etherlinkNodeBalancesProvider/etherlinkNodeBalancesProvider.ts b/src/bridgeDataProviders/balancesBridgeDataProvider/etherlinkNodeBalancesProvider/etherlinkNodeBalancesProvider.ts index 4ecd3d7..516c947 100644 --- a/src/bridgeDataProviders/balancesBridgeDataProvider/etherlinkNodeBalancesProvider/etherlinkNodeBalancesProvider.ts +++ b/src/bridgeDataProviders/balancesBridgeDataProvider/etherlinkNodeBalancesProvider/etherlinkNodeBalancesProvider.ts @@ -1,10 +1,12 @@ import type { BalanceData, RPCNodeResponse } from './dto'; import { EtherlinkNodeRPCError, EtherlinkNodeTokenBalanceNotSupported } from './error'; import { RemoteService } from '../../../common'; -import type { NativeEtherlinkToken } from '../../../etherlink'; import { getErrorLogMessage, loggerProvider } from '../../../logging'; +import type { NativeEtherlinkToken } from '../../../tokens'; +import { guards } from '../../../utils'; import type { AccountTokenBalance, AccountTokenBalances } from '../accountTokenBalances'; import type { BalancesBridgeDataProvider } from '../balancesBridgeDataProvider'; +import type { BalancesFetchOptions } from '../balancesFetchOptions'; export class EtherlinkNodeBalancesProvider extends RemoteService implements BalancesBridgeDataProvider { async getBalance(accountAddress: string, token: NativeEtherlinkToken): Promise { @@ -19,17 +21,13 @@ export class EtherlinkNodeBalancesProvider extends RemoteService implements Bala async getBalances(accountAddress: string): Promise; async getBalances(accountAddress: string, tokens: readonly NativeEtherlinkToken[]): Promise; - async getBalances(accountAddress: string, offset: number, limit: number): Promise; - async getBalances(accountAddress: string, tokensOrOffset?: readonly NativeEtherlinkToken[] | number, limit?: number): Promise; - async getBalances( - accountAddress: string, - tokensOrOffset?: readonly NativeEtherlinkToken[] | number, - _limit?: number - ): Promise { - const isAllTokens = typeof tokensOrOffset === 'number' || !tokensOrOffset; + async getBalances(accountAddress: string, fetchOptions: BalancesFetchOptions): Promise; + async getBalances(accountAddress: string, tokensOrFetchOptions?: readonly NativeEtherlinkToken[] | BalancesFetchOptions): Promise; + async getBalances(accountAddress: string, tokensOrFetchOptions?: readonly NativeEtherlinkToken[] | BalancesFetchOptions): Promise { + const isAllTokens = !guards.isReadonlyArray(tokensOrFetchOptions); - if (!isAllTokens && tokensOrOffset.length && tokensOrOffset[0]!.type !== 'native') { - const error = new EtherlinkNodeTokenBalanceNotSupported(tokensOrOffset[0]!); + if (!isAllTokens && tokensOrFetchOptions.length && tokensOrFetchOptions[0]!.type !== 'native') { + const error = new EtherlinkNodeTokenBalanceNotSupported(tokensOrFetchOptions[0]!); loggerProvider.logger.error(error); throw error; } diff --git a/src/bridgeDataProviders/balancesBridgeDataProvider/index.ts b/src/bridgeDataProviders/balancesBridgeDataProvider/index.ts index 2d168ce..61776e7 100644 --- a/src/bridgeDataProviders/balancesBridgeDataProvider/index.ts +++ b/src/bridgeDataProviders/balancesBridgeDataProvider/index.ts @@ -1,4 +1,5 @@ export type { AccountTokenBalance, AccountTokenBalances, TokenBalanceInfo } from './accountTokenBalances'; export type { BalancesBridgeDataProvider } from './balancesBridgeDataProvider'; +export type { BalancesFetchOptions } from './balancesFetchOptions'; export { TzKTBalancesProvider } from './tzKTBalancesProvider'; export { EtherlinkNodeBalancesProvider } from './etherlinkNodeBalancesProvider'; diff --git a/src/bridgeDataProviders/balancesBridgeDataProvider/tzKTBalancesProvider/errors.ts b/src/bridgeDataProviders/balancesBridgeDataProvider/tzKTBalancesProvider/errors.ts index 7376daa..758a515 100644 --- a/src/bridgeDataProviders/balancesBridgeDataProvider/tzKTBalancesProvider/errors.ts +++ b/src/bridgeDataProviders/balancesBridgeDataProvider/tzKTBalancesProvider/errors.ts @@ -1,5 +1,5 @@ import { TokenBridgeError } from '../../../common'; -import type { TezosToken } from '../../../tezos'; +import type { TezosToken } from '../../../tokens'; import { tokenUtils } from '../../../utils'; export class TzKTTokenBalanceNotSupported extends TokenBridgeError { diff --git a/src/bridgeDataProviders/balancesBridgeDataProvider/tzKTBalancesProvider/tzKTBalancesProvider.ts b/src/bridgeDataProviders/balancesBridgeDataProvider/tzKTBalancesProvider/tzKTBalancesProvider.ts index 70af0a4..4124f8f 100644 --- a/src/bridgeDataProviders/balancesBridgeDataProvider/tzKTBalancesProvider/tzKTBalancesProvider.ts +++ b/src/bridgeDataProviders/balancesBridgeDataProvider/tzKTBalancesProvider/tzKTBalancesProvider.ts @@ -3,10 +3,11 @@ import { TzKTTokenBalanceNotSupported } from './errors'; import * as mappers from './mappers'; import { RemoteService } from '../../../common'; import { getTokenLogMessage, loggerProvider } from '../../../logging'; -import type { TezosToken, NativeTezosToken, NonNativeTezosToken, FA2TezosToken } from '../../../tezos'; +import type { TezosToken, NativeTezosToken, NonNativeTezosToken, FA2TezosToken } from '../../../tokens'; import { guards, tokenUtils } from '../../../utils'; import type { AccountTokenBalance, AccountTokenBalances, TokenBalanceInfo } from '../accountTokenBalances'; import type { BalancesBridgeDataProvider } from '../balancesBridgeDataProvider'; +import type { BalancesFetchOptions } from '../balancesFetchOptions'; export class TzKTBalancesProvider extends RemoteService implements BalancesBridgeDataProvider { protected static readonly defaultLoadDataLimit = 10000; @@ -31,16 +32,12 @@ export class TzKTBalancesProvider extends RemoteService implements BalancesBridg async getBalances(accountAddress: string): Promise; async getBalances(accountAddress: string, tokens: readonly TezosToken[]): Promise; - async getBalances(accountAddress: string, offset: number, limit: number): Promise; - async getBalances(accountAddress: string, tokensOrOffset?: readonly TezosToken[] | number, limit?: number): Promise; - async getBalances( - accountAddress: string, - tokensOrOffset?: readonly TezosToken[] | number, - limit?: number - ): Promise { - return typeof tokensOrOffset === 'number' || !tokensOrOffset - ? this.getAllTokenBalances(accountAddress, tokensOrOffset, limit) - : this.getTezosTokenBalances(accountAddress, tokensOrOffset); + async getBalances(accountAddress: string, fetchOptions: BalancesFetchOptions): Promise; + async getBalances(accountAddress: string, tokensOrFetchOptions?: readonly TezosToken[] | BalancesFetchOptions): Promise; + async getBalances(accountAddress: string, tokensOrFetchOptions?: readonly TezosToken[] | BalancesFetchOptions): Promise { + return guards.isReadonlyArray(tokensOrFetchOptions) + ? this.getTezosTokenBalances(accountAddress, tokensOrFetchOptions) + : this.getAllTokenBalances(accountAddress, tokensOrFetchOptions?.offset, tokensOrFetchOptions?.limit); } protected async getAllTokenBalances(accountAddress: string, offset?: number, limit?: number) { diff --git a/src/bridgeDataProviders/defaultDataProvider/defaultDataProvider.ts b/src/bridgeDataProviders/defaultDataProvider/defaultDataProvider.ts index bc4444a..cd5de93 100644 --- a/src/bridgeDataProviders/defaultDataProvider/defaultDataProvider.ts +++ b/src/bridgeDataProviders/defaultDataProvider/defaultDataProvider.ts @@ -1,15 +1,17 @@ import type { DefaultDataProviderOptions } from './defaultDataProviderOptions'; import type { BridgeTokenTransfer, TokenPair } from '../../bridgeCore'; -import type { EtherlinkToken, NativeEtherlinkToken, NonNativeEtherlinkToken } from '../../etherlink'; -import type { TezosToken, NativeTezosToken, NonNativeTezosToken } from '../../tezos'; +import type { + TezosToken, NativeTezosToken, NonNativeTezosToken, + EtherlinkToken, NativeEtherlinkToken, NonNativeEtherlinkToken +} from '../../tokens'; import { guards, memoize } from '../../utils'; import { TzKTBalancesProvider, EtherlinkNodeBalancesProvider, - type AccountTokenBalance, type AccountTokenBalances, type BalancesBridgeDataProvider + type AccountTokenBalance, type AccountTokenBalances, type BalancesBridgeDataProvider, type BalancesFetchOptions } from '../balancesBridgeDataProvider'; import { DipDupBridgeDataProvider } from '../dipDupBridgeDataProvider'; -import { LocalTokensBridgeDataProvider, type TokensBridgeDataProvider } from '../tokensBridgeDataProvider'; -import type { TransfersBridgeDataProvider } from '../transfersBridgeDataProvider'; +import { LocalTokensBridgeDataProvider, type TokensBridgeDataProvider, type TokensFetchOptions } from '../tokensBridgeDataProvider'; +import type { TransfersBridgeDataProvider, TransfersFetchOptions } from '../transfersBridgeDataProvider'; interface TokenGroups { nativeTokens: Array; @@ -46,41 +48,39 @@ export class DefaultDataProvider implements TransfersBridgeDataProvider, Balance return this.dipDupBridgeDataProvider.events; } - async getTokenTransfer(operationHash: string): Promise; - async getTokenTransfer(tokenTransfer: BridgeTokenTransfer): Promise; - async getTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise; - async getTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise { - return this.dipDupBridgeDataProvider.getTokenTransfer(operationHashOrTokenTransfer); + async getTokenTransfer(tokenTransferId: string): Promise { + return this.dipDupBridgeDataProvider.getTokenTransfer(tokenTransferId); } async getTokenTransfers(): Promise; - async getTokenTransfers(offset: number, limit: number): Promise; - async getTokenTransfers(offset?: number, limit?: number): Promise; - async getTokenTransfers(offset?: number, limit?: number): Promise { - return this.dipDupBridgeDataProvider.getTokenTransfers(offset, limit); + async getTokenTransfers(fetchOptions: TransfersFetchOptions): Promise; + async getTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise; + async getTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise { + return this.dipDupBridgeDataProvider.getTokenTransfers(fetchOptions); } async getAccountTokenTransfers(accountAddress: string): Promise; async getAccountTokenTransfers(accountAddresses: readonly string[]): Promise; - async getAccountTokenTransfers(accountAddress: string, offset: number, limit: number): Promise; - async getAccountTokenTransfers(accountAddresses: readonly string[], offset: number, limit: number): Promise; - async getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], offset?: number, limit?: number): Promise; - async getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], offset?: number, limit?: number): Promise { - return this.dipDupBridgeDataProvider.getAccountTokenTransfers(accountAddressOfAddresses, offset, limit); + async getAccountTokenTransfers(accountAddress: string, fetchOptions: TransfersFetchOptions): Promise; + async getAccountTokenTransfers(accountAddresses: readonly string[], fetchOptions: TransfersFetchOptions): Promise; + async getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], fetchOptions?: TransfersFetchOptions): Promise; + async getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], fetchOptions?: TransfersFetchOptions): Promise { + return this.dipDupBridgeDataProvider.getAccountTokenTransfers(accountAddressOfAddresses, fetchOptions); } - subscribeToTokenTransfer(operationHash: string): void; - subscribeToTokenTransfer(tokenTransfer: BridgeTokenTransfer): void; - subscribeToTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; - subscribeToTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { - return this.dipDupBridgeDataProvider.subscribeToTokenTransfer(operationHashOrTokenTransfer); + async getOperationTokenTransfers(operationHash: string): Promise; + async getOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): Promise; + async getOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise; + async getOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise { + return this.dipDupBridgeDataProvider.getOperationTokenTransfers(operationHashOrTokenTransfer); } - unsubscribeFromTokenTransfer(operationHash: string): void; - unsubscribeFromTokenTransfer(tokenTransfer: BridgeTokenTransfer): void; - unsubscribeFromTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; - unsubscribeFromTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { - return this.dipDupBridgeDataProvider.unsubscribeFromTokenTransfer(operationHashOrTokenTransfer); + subscribeToTokenTransfer(tokenTransferId: string): void { + return this.dipDupBridgeDataProvider.subscribeToTokenTransfer(tokenTransferId); + } + + unsubscribeFromTokenTransfer(tokenTransferId: string): void { + return this.dipDupBridgeDataProvider.unsubscribeFromTokenTransfer(tokenTransferId); } subscribeToTokenTransfers(): void { @@ -105,17 +105,31 @@ export class DefaultDataProvider implements TransfersBridgeDataProvider, Balance return this.dipDupBridgeDataProvider.unsubscribeFromAccountTokenTransfers(accountAddressOrAddresses); } + subscribeToOperationTokenTransfers(operationHash: string): void; + subscribeToOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): void; + subscribeToOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + subscribeToOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { + return this.dipDupBridgeDataProvider.subscribeToOperationTokenTransfers(operationHashOrTokenTransfer); + } + + unsubscribeFromOperationTokenTransfers(operationHash: string): void; + unsubscribeFromOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): void; + unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { + return this.dipDupBridgeDataProvider.unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer); + } + unsubscribeFromAllSubscriptions(): void { return this.dipDupBridgeDataProvider.unsubscribeFromAllSubscriptions(); } async getBalances(accountAddress: string): Promise; async getBalances(accountAddress: string, tokens: ReadonlyArray): Promise; - async getBalances(accountAddress: string, offset: number, limit: number): Promise; - async getBalances(accountAddress: string, tokensOrOffset?: number | ReadonlyArray | undefined, limit?: number | undefined): Promise; - async getBalances(accountAddress: string, tokensOrOffset?: number | ReadonlyArray | undefined, limit?: number | undefined): Promise { + async getBalances(accountAddress: string, fetchOptions: BalancesFetchOptions): Promise; + async getBalances(accountAddress: string, tokensOrFetchOptions?: ReadonlyArray | BalancesFetchOptions): Promise; + async getBalances(accountAddress: string, tokensOrFetchOptions?: ReadonlyArray | BalancesFetchOptions): Promise { const isEtherlinkAccount = this.isEtherlinkAccount(accountAddress); - const tokenGroups = typeof tokensOrOffset === 'number' || !tokensOrOffset ? null : this.groupTokens(tokensOrOffset); + const tokenGroups = guards.isReadonlyArray(tokensOrFetchOptions) ? this.groupTokens(tokensOrFetchOptions) : null; const promises: Array> = []; if (isEtherlinkAccount) { @@ -126,9 +140,9 @@ export class DefaultDataProvider implements TransfersBridgeDataProvider, Balance promises.push(this.dipDupBridgeDataProvider.getBalances(accountAddress, tokenGroups.nonNativeEtherlinkTokens)); } else { - if (!tokensOrOffset) + if (!(tokensOrFetchOptions as BalancesFetchOptions | undefined)?.offset) promises.push(this.etherlinkNodeBalancesDataProvider.getBalances(accountAddress, nativeTokenArray)); - promises.push(this.dipDupBridgeDataProvider.getBalances(accountAddress, tokensOrOffset as number, limit)); + promises.push(this.dipDupBridgeDataProvider.getBalances(accountAddress, tokensOrFetchOptions as BalancesFetchOptions | undefined)); } } else { @@ -139,7 +153,7 @@ export class DefaultDataProvider implements TransfersBridgeDataProvider, Balance promises.push(this.tzKTBalancesDataProvider.getBalances(accountAddress, tokenGroups.nonNativeTezosTokens)); } else { - promises.push(this.tzKTBalancesDataProvider.getBalances(accountAddress, tokensOrOffset as number, limit)); + promises.push(this.tzKTBalancesDataProvider.getBalances(accountAddress, tokensOrFetchOptions as BalancesFetchOptions | undefined)); } } @@ -174,10 +188,10 @@ export class DefaultDataProvider implements TransfersBridgeDataProvider, Balance } getRegisteredTokenPairs(): Promise; - getRegisteredTokenPairs(offset: number, limit: number): Promise; - getRegisteredTokenPairs(offset?: number, limit?: number): Promise; - getRegisteredTokenPairs(offset?: number, limit?: number): Promise { - return this.bridgeDataProvider.getRegisteredTokenPairs(offset, limit); + getRegisteredTokenPairs(fetchOptions: TokensFetchOptions): Promise; + getRegisteredTokenPairs(fetchOptions?: TokensFetchOptions): Promise; + getRegisteredTokenPairs(fetchOptions?: TokensFetchOptions): Promise { + return this.bridgeDataProvider.getRegisteredTokenPairs(fetchOptions); } protected groupTokens = memoize((tokens: ReadonlyArray): TokenGroups => { diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupBridgeDataProvider.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupBridgeDataProvider.ts index 35cfa2f..3aadfc1 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupBridgeDataProvider.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupBridgeDataProvider.ts @@ -1,22 +1,23 @@ import type { DipDupBridgeDataProviderOptions } from './dipDupBridgeDataProviderOptions'; import { DipDupGraphQLQueryBuilder } from './dipDupGraphQLQueryBuilder'; import type { GraphQLResponse, BridgeOperationsDto, TokenBalancesDto } from './dtos'; -import { DipDupAutoUpdateIsDisabledError, DipDupGraphQLError, DipDupTokenBalanceNotSupported } from './errors'; +import { DipDupAutoUpdateIsDisabledError, DipDupGraphQLError, DipDupTokenBalanceNotSupported, DipDupTokenTransferIdInvalid } from './errors'; import * as mappers from './mappers'; import { DipDupWebSocketClient, type DipDupWebSocketResponseDto } from './webSocket'; import { loggerProvider } from '../..'; import { BridgeTokenTransferKind, type BridgeTokenTransfer } from '../../bridgeCore'; import { EventEmitter, RemoteService, ToEventEmitter } from '../../common'; -import type { NonNativeEtherlinkToken } from '../../etherlink'; import { getErrorLogMessage, getBridgeTokenTransferLogMessage, getDetailedBridgeTokenTransferLogMessage, - getTokenLogMessage + getTokenLogMessage, + getBridgeTokenTransferIdLogMessage } from '../../logging'; -import { bridgeUtils } from '../../utils'; -import type { BalancesBridgeDataProvider, AccountTokenBalance, AccountTokenBalances } from '../balancesBridgeDataProvider'; -import type { TransfersBridgeDataProvider } from '../transfersBridgeDataProvider'; +import type { NonNativeEtherlinkToken } from '../../tokens'; +import { bridgeUtils, guards } from '../../utils'; +import type { BalancesBridgeDataProvider, AccountTokenBalance, AccountTokenBalances, BalancesFetchOptions } from '../balancesBridgeDataProvider'; +import type { TransfersBridgeDataProvider, TransfersFetchOptions } from '../transfersBridgeDataProvider'; export class DipDupBridgeDataProvider extends RemoteService implements TransfersBridgeDataProvider, BalancesBridgeDataProvider, Disposable { protected static readonly defaultLoadDataLimit = 100; @@ -56,27 +57,22 @@ export class DipDupBridgeDataProvider extends RemoteService implements Transfers throw error; } - async getTokenTransfer(operationHash: string): Promise; - async getTokenTransfer(tokenTransfer: BridgeTokenTransfer): Promise; - async getTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise; - async getTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise { - const operationHash = typeof operationHashOrTokenTransfer === 'string' - ? operationHashOrTokenTransfer - : (operationHashOrTokenTransfer.kind === BridgeTokenTransferKind.Deposit) - ? operationHashOrTokenTransfer.tezosOperation.hash - : operationHashOrTokenTransfer.etherlinkOperation.hash; + async getTokenTransfer(tokenTransferId: string): Promise { + loggerProvider.logger.log('Getting token transfer by the token transfer Id:', tokenTransferId); - loggerProvider.logger.log('Getting token transfer by the operation hash:', operationHash); + const operationData = bridgeUtils.convertTokenTransferIdToOperationData(tokenTransferId); + if (!operationData) + throw new DipDupTokenTransferIdInvalid(tokenTransferId); const bridgeOperationsResponse = await this.fetch>('/v1/graphql', 'json', { method: 'POST', body: JSON.stringify({ - query: this.dipDupGraphQLQueryBuilder.getTokenTransferQuery(operationHash) + query: this.dipDupGraphQLQueryBuilder.getTokenTransferQuery(operationData[0], operationData[1], operationData[2]) }) }); this.ensureNoDipDupGraphQLErrors(bridgeOperationsResponse); - loggerProvider.logger.log('Token transfer has been received by the operation hash:', operationHash); + loggerProvider.logger.log('Token transfer has been received by the token transfer Id:', tokenTransferId); const tokenTransfer = mappers.mapBridgeOperationsDtoToBridgeTokenTransfer(bridgeOperationsResponse.data)[0]; @@ -87,51 +83,69 @@ export class DipDupBridgeDataProvider extends RemoteService implements Transfers } async getTokenTransfers(): Promise; - async getTokenTransfers(offset: number, limit: number): Promise; - async getTokenTransfers(offset?: number, limit?: number): Promise; - async getTokenTransfers(offset?: number, limit?: number): Promise { - return this.getTokenTransfersInternal(undefined, offset, limit); + async getTokenTransfers(fetchOptions: TransfersFetchOptions): Promise; + async getTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise; + async getTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise { + return this.getTokenTransfersInternal(undefined, fetchOptions); } async getAccountTokenTransfers(accountAddress: string): Promise; async getAccountTokenTransfers(accountAddresses: readonly string[]): Promise; - async getAccountTokenTransfers(accountAddress: string, offset: number, limit: number): Promise; - async getAccountTokenTransfers(accountAddresses: readonly string[], offset: number, limit: number): Promise; - async getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], offset?: number, limit?: number): Promise; - async getAccountTokenTransfers( - accountAddressOfAddresses: string | readonly string[], - offset?: number, - limit?: number - ): Promise { - return this.getTokenTransfersInternal(accountAddressOfAddresses, offset, limit); + async getAccountTokenTransfers(accountAddress: string, fetchOptions: TransfersFetchOptions): Promise; + async getAccountTokenTransfers(accountAddresses: readonly string[], fetchOptions: TransfersFetchOptions): Promise; + async getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], fetchOptions?: TransfersFetchOptions): Promise; + async getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], fetchOptions?: TransfersFetchOptions): Promise { + return this.getTokenTransfersInternal(accountAddressOfAddresses, fetchOptions); } - subscribeToTokenTransfer(operationHash: string): void; - subscribeToTokenTransfer(tokenTransfer: BridgeTokenTransfer): void; - subscribeToTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; - subscribeToTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { - this.startDipDupWebSocketClientIfNeeded(); + async getOperationTokenTransfers(operationHash: string): Promise; + async getOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): Promise; + async getOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise; + async getOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise { const operationHash = typeof operationHashOrTokenTransfer === 'string' ? operationHashOrTokenTransfer - : bridgeUtils.getInitialOperationHash(operationHashOrTokenTransfer); + : (operationHashOrTokenTransfer.kind === BridgeTokenTransferKind.Deposit) + ? operationHashOrTokenTransfer.tezosOperation.hash + : operationHashOrTokenTransfer.etherlinkOperation.hash; + + loggerProvider.logger.log('Getting token transfer by the operation hash:', operationHash); - loggerProvider.logger.log('Subscribe to the token transfer by the initial operation:', operationHash); + const bridgeOperationsResponse = await this.fetch>('/v1/graphql', 'json', { + method: 'POST', + body: JSON.stringify({ + query: this.dipDupGraphQLQueryBuilder.getOperationTokenTransfersQuery(operationHash) + }) + }); + this.ensureNoDipDupGraphQLErrors(bridgeOperationsResponse); + + loggerProvider.logger.log(`Token transfer (${bridgeOperationsResponse.data.bridge_operation.length}) has been received by the operation hash:`, operationHash); + + const tokenTransfers = mappers.mapBridgeOperationsDtoToBridgeTokenTransfer(bridgeOperationsResponse.data); + + return tokenTransfers; + } - const subscription = this.dipDupGraphQLQueryBuilder.getTokenTransferSubscription(operationHash); + subscribeToTokenTransfer(tokenTransferId: string): void { + this.startDipDupWebSocketClientIfNeeded(); + + loggerProvider.logger.log('Subscribe to the token transfer by the token transfer Id:', tokenTransferId); + + const operationData = bridgeUtils.convertTokenTransferIdToOperationData(tokenTransferId); + if (!operationData) + throw new DipDupTokenTransferIdInvalid(tokenTransferId); + + const subscription = this.dipDupGraphQLQueryBuilder.getTokenTransferSubscription(operationData[0], operationData[1], operationData[2]); this.dipDupWebSocketClient.subscribe(subscription); } - unsubscribeFromTokenTransfer(operationHash: string): void; - unsubscribeFromTokenTransfer(tokenTransfer: BridgeTokenTransfer): void; - unsubscribeFromTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; - unsubscribeFromTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { - const operationHash = typeof operationHashOrTokenTransfer === 'string' - ? operationHashOrTokenTransfer - : bridgeUtils.getInitialOperationHash(operationHashOrTokenTransfer); + unsubscribeFromTokenTransfer(tokenTransferId: string): void { + loggerProvider.logger.log('Unsubscribe from the token transfer by the token transfer Id:', tokenTransferId); - loggerProvider.logger.log('Unsubscribe from the token transfer by the initial operation:', operationHash); + const operationData = bridgeUtils.convertTokenTransferIdToOperationData(tokenTransferId); + if (!operationData) + throw new DipDupTokenTransferIdInvalid(tokenTransferId); - const subscription = this.dipDupGraphQLQueryBuilder.getTokenTransferSubscription(operationHash); + const subscription = this.dipDupGraphQLQueryBuilder.getTokenTransferSubscription(operationData[0], operationData[1], operationData[2]); this.dipDupWebSocketClient.unsubscribe(subscription); } @@ -159,6 +173,35 @@ export class DipDupBridgeDataProvider extends RemoteService implements Transfers this.unsubscribeFromTokenTransfersInternal(accountAddressOrAddresses); } + subscribeToOperationTokenTransfers(operationHash: string): void; + subscribeToOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): void; + subscribeToOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + subscribeToOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { + this.startDipDupWebSocketClientIfNeeded(); + const operationHash = typeof operationHashOrTokenTransfer === 'string' + ? operationHashOrTokenTransfer + : bridgeUtils.getInitialOperation(operationHashOrTokenTransfer).hash; + + loggerProvider.logger.log('Subscribe to the token transfers by the initial operation:', operationHash); + + const subscription = this.dipDupGraphQLQueryBuilder.getOperationTokenTransfersSubscription(operationHash); + this.dipDupWebSocketClient.subscribe(subscription); + } + + unsubscribeFromOperationTokenTransfers(operationHash: string): void; + unsubscribeFromOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): void; + unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { + const operationHash = typeof operationHashOrTokenTransfer === 'string' + ? operationHashOrTokenTransfer + : bridgeUtils.getInitialOperation(operationHashOrTokenTransfer).hash; + + loggerProvider.logger.log('Unsubscribe from the token transfers by the initial operation:', operationHash); + + const subscription = this.dipDupGraphQLQueryBuilder.getOperationTokenTransfersSubscription(operationHash); + this.dipDupWebSocketClient.unsubscribe(subscription); + } + unsubscribeFromAllSubscriptions(): void { this.dipDupWebSocketClient.unsubscribeFromAllSubscriptions(); this.subscribedAddresses.clear(); @@ -199,32 +242,28 @@ export class DipDupBridgeDataProvider extends RemoteService implements Transfers async getBalances(accountAddress: string): Promise; async getBalances(accountAddress: string, tokens: readonly NonNativeEtherlinkToken[]): Promise; - async getBalances(accountAddress: string, offset: number, limit: number): Promise; - async getBalances(accountAddress: string, tokensOrOffset?: readonly NonNativeEtherlinkToken[] | number, limit?: number): Promise; - async getBalances( - accountAddress: string, - tokensOrOffset?: readonly NonNativeEtherlinkToken[] | number, - limit?: number - ): Promise { + async getBalances(accountAddress: string, fetchOptions: BalancesFetchOptions): Promise; + async getBalances(accountAddress: string, tokensOrFetchOptions?: readonly NonNativeEtherlinkToken[] | BalancesFetchOptions): Promise; + async getBalances(accountAddress: string, tokensOrFetchOptions?: readonly NonNativeEtherlinkToken[] | BalancesFetchOptions): Promise { let query: string; - const isAllTokens = typeof tokensOrOffset === 'number' || !tokensOrOffset; + const isAllTokens = !guards.isReadonlyArray(tokensOrFetchOptions); if (isAllTokens) { query = this.dipDupGraphQLQueryBuilder.getAllTokenBalancesQuery( accountAddress, - this.getPreparedOffsetParameter(tokensOrOffset), - this.getPreparedLimitParameter(limit), + this.getPreparedOffsetParameter(tokensOrFetchOptions), + this.getPreparedLimitParameter(tokensOrFetchOptions), ); } else { - const tokenAddresses = tokensOrOffset.map(token => token.address); + const tokenAddresses = tokensOrFetchOptions.map(token => token.address); query = this.dipDupGraphQLQueryBuilder.getTokenBalancesQuery( accountAddress, tokenAddresses ); } - loggerProvider.lazyLogger.log?.(`Getting balances of the ${isAllTokens ? 'all' : getTokenLogMessage(tokensOrOffset)} tokens for the ${accountAddress} address`); + loggerProvider.lazyLogger.log?.(`Getting balances of the ${isAllTokens ? 'all' : getTokenLogMessage(tokensOrFetchOptions)} tokens for the ${accountAddress} address`); const tokenBalancesResponse = await this.fetch>('/v1/graphql', 'json', { method: 'POST', @@ -234,7 +273,7 @@ export class DipDupBridgeDataProvider extends RemoteService implements Transfers }); this.ensureNoDipDupGraphQLErrors(tokenBalancesResponse); - loggerProvider.lazyLogger.log?.(`The balances of the ${isAllTokens ? 'all' : getTokenLogMessage(tokensOrOffset)} tokens for the ${accountAddress} address has been received`); + loggerProvider.lazyLogger.log?.(`The balances of the ${isAllTokens ? 'all' : getTokenLogMessage(tokensOrFetchOptions)} tokens for the ${accountAddress} address has been received`); loggerProvider.logger.debug('Mapping the tokenBalancesDTO to AccountTokenBalances...'); const accountTokenBalances = mappers.mapTokenBalancesDtoToAccountTokenBalances(tokenBalancesResponse.data, accountAddress); @@ -263,12 +302,10 @@ export class DipDupBridgeDataProvider extends RemoteService implements Transfers protected async getTokenTransfersInternal( addressOrAddresses: string | readonly string[] | undefined | null, - offset: number | undefined | null, - limit: number | undefined | null + fetchOptions?: TransfersFetchOptions | undefined | null ): Promise { - - offset = this.getPreparedOffsetParameter(offset); - limit = this.getPreparedLimitParameter(limit); + const offset = this.getPreparedOffsetParameter(fetchOptions); + const limit = this.getPreparedLimitParameter(fetchOptions); loggerProvider.lazyLogger.log?.(addressOrAddresses ? `Getting token transfers for ${typeof addressOrAddresses === 'string' @@ -385,7 +422,7 @@ export class DipDupBridgeDataProvider extends RemoteService implements Transfers if (!tokenTransfer) continue; - loggerProvider.lazyLogger.log?.(`The ${bridgeUtils.getInitialOperationHash(tokenTransfer)} token transfer has been ${isCreated ? 'created' : 'updated'}.`); + loggerProvider.lazyLogger.log?.(`The ${getBridgeTokenTransferIdLogMessage(tokenTransfer)} token transfer has been ${isCreated ? 'created' : 'updated'}.`); loggerProvider.lazyLogger.log?.(getBridgeTokenTransferLogMessage(tokenTransfer)); loggerProvider.lazyLogger.debug?.(getDetailedBridgeTokenTransferLogMessage(tokenTransfer)); @@ -443,11 +480,15 @@ export class DipDupBridgeDataProvider extends RemoteService implements Transfers return [previousSubscribedAddressesSize, updatedSubscribedAddressesSize]; } - private getPreparedOffsetParameter(offset: number | undefined | null): number { + private getPreparedOffsetParameter(offsetOrFetchOptions: number | undefined | null | BalancesFetchOptions): number { + const offset = typeof offsetOrFetchOptions === 'number' ? offsetOrFetchOptions : offsetOrFetchOptions?.offset; + return offset && offset > 0 ? offset : 0; } - private getPreparedLimitParameter(limit: number | undefined | null): number { + private getPreparedLimitParameter(limitOrFetchOptions: number | undefined | null | BalancesFetchOptions): number { + const limit = typeof limitOrFetchOptions === 'number' ? limitOrFetchOptions : limitOrFetchOptions?.offset; + return limit && limit > 0 ? limit : DipDupBridgeDataProvider.defaultLoadDataLimit; } diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/dipDupGraphQLQueryBuilder.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/dipDupGraphQLQueryBuilder.ts index 2d1c844..220cffa 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/dipDupGraphQLQueryBuilder.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/dipDupGraphQLQueryBuilder.ts @@ -24,12 +24,26 @@ export class DipDupGraphQLQueryBuilder { ) { } - getTokenTransferQuery(operationHash: string): GraphQLQuery { - return this.getTokenTransferQueryOrSubscription(operationHash, true); + getTokenTransferQuery(etherlinkOperationHash: string, logIndex: number): GraphQLQuery; + getTokenTransferQuery(tezosOperationHash: string, counter: number, nonce: number | null): GraphQLQuery; + getTokenTransferQuery(operationHash: string, counterOrLogIndex?: number, nonce?: number | null): GraphQLQuery; + getTokenTransferQuery(operationHash: string, counterOrLogIndex?: number, nonce?: number | null): GraphQLQuery { + return this.getOperationTokenTransfersQueryOrSubscription(true, operationHash, counterOrLogIndex, nonce); } - getTokenTransferSubscription(operationHash: string): GraphQLQuery { - return this.getTokenTransferQueryOrSubscription(operationHash, false); + getTokenTransferSubscription(etherlinkOperationHash: string, logIndex: number): GraphQLQuery; + getTokenTransferSubscription(tezosOperationHash: string, counter: number, nonce: number | null): GraphQLQuery; + getTokenTransferSubscription(operationHash: string, counterOrLogIndex?: number, nonce?: number | null): GraphQLQuery; + getTokenTransferSubscription(operationHash: string, counterOrLogIndex?: number, nonce?: number | null): GraphQLQuery { + return this.getOperationTokenTransfersQueryOrSubscription(false, operationHash, counterOrLogIndex, nonce); + } + + getOperationTokenTransfersQuery(operationHash: string): GraphQLQuery { + return this.getOperationTokenTransfersQueryOrSubscription(true, operationHash); + } + + getOperationTokenTransfersSubscription(operationHash: string): GraphQLQuery { + return this.getOperationTokenTransfersQueryOrSubscription(false, operationHash); } getTokenTransfersQuery( @@ -96,24 +110,48 @@ export class DipDupGraphQLQueryBuilder { }`; } - protected getTokenTransferQueryOrSubscription(operationHash: string, isQuery: boolean): GraphQLQuery { + protected getOperationTokenTransfersQueryOrSubscription(isQuery: boolean, operationHash: string): GraphQLQuery; + protected getOperationTokenTransfersQueryOrSubscription(isQuery: boolean, etherlinkOperationHash: string, logIndex: number): GraphQLQuery; + protected getOperationTokenTransfersQueryOrSubscription(isQuery: boolean, tezosOperationHash: string, counter: number, nonce: number | null): GraphQLQuery; + protected getOperationTokenTransfersQueryOrSubscription(isQuery: boolean, operationHash: string, counterOrLogIndex?: number, nonce?: number | null): GraphQLQuery; + protected getOperationTokenTransfersQueryOrSubscription(isQuery: boolean, operationHash: string, counterOrLogIndex?: number, nonce?: number | null): GraphQLQuery { const queryType = isQuery ? 'query' : 'subscription'; let whereArgument: string; if (this.isEtherlinkTransaction(operationHash)) { operationHash = this.prepareEtherlinkHexValue(operationHash, false); + const logIndexCondition = typeof counterOrLogIndex === 'number' + ? `log_index: { _eq: ${counterOrLogIndex} }` + : ''; + const l2TransactionCondition = `l2_transaction: { + transaction_hash: { _eq: "${operationHash}" } + ${logIndexCondition} + }`; + whereArgument = `where: { _or : [ - { withdrawal: { l2_transaction: { transaction_hash: { _eq: "${operationHash}" } } } } - { deposit: { l2_transaction: { transaction_hash: { _eq: "${operationHash}" } } } } + { withdrawal: { ${l2TransactionCondition} } } + { deposit: { ${l2TransactionCondition} } } ] }`; } else { + const counterCondition = typeof counterOrLogIndex === 'number' + ? `counter: { _eq: ${counterOrLogIndex} }` + : ''; + const nonceCondition = typeof nonce === 'number' + ? `nonce: { _eq: ${nonce} }` + : ''; + const l1TransactionCondition = `l1_transaction: { + operation_hash: { _eq: "${operationHash}" } + ${counterCondition} + ${nonceCondition} + }`; + whereArgument = `where: { _or : [ - { deposit: { l1_transaction: { operation_hash: { _eq: "${operationHash}" } } } } - { withdrawal: { l1_transaction: { operation_hash: { _eq: "${operationHash}" } } } } + { deposit: { ${l1TransactionCondition} } } + { withdrawal: { ${l1TransactionCondition} } } ] }`; } diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/queryParts/bridgeDepositFields.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/queryParts/bridgeDepositFields.ts index 3507b51..7339e37 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/queryParts/bridgeDepositFields.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/queryParts/bridgeDepositFields.ts @@ -2,6 +2,8 @@ export const bridgeDepositFields = ` l1_transaction { level operation_hash + counter + nonce amount ticket { token { @@ -22,6 +24,7 @@ l1_transaction { l2_transaction { level transaction_hash + log_index amount l2_token { id diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/queryParts/bridgeWithdrawalFields.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/queryParts/bridgeWithdrawalFields.ts index e881420..3948388 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/queryParts/bridgeWithdrawalFields.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/dipDupGraphQLQueryBuilder/queryParts/bridgeWithdrawalFields.ts @@ -2,11 +2,14 @@ export const bridgeWithdrawalFields = ` l1_transaction { level operation_hash + counter + nonce timestamp } l2_transaction { level transaction_hash + log_index amount l2_token { id @@ -28,5 +31,6 @@ l2_transaction { hash } proof + cemented_at } }`; diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/dtos.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/dtos.ts index ccdbb81..28230ac 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/dtos.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/dtos.ts @@ -17,6 +17,8 @@ interface TezosTicketDto { interface DepositL1TransactionDto { level: number; operation_hash: string; + counter: number; + nonce: number | null; amount: string; ticket: TezosTicketDto; l1_account: string; @@ -28,6 +30,7 @@ interface DepositL1TransactionDto { interface DepositL2TransactionDto { level: number; transaction_hash: string; + log_index: number; amount: string; l2_token: { id: string; @@ -42,13 +45,16 @@ export interface BridgeDepositDto { interface WithdrawalL1TransactionDto { level: number; - operation_hash: string + operation_hash: string; + counter: number; + nonce: number | null; timestamp: string; } interface WithdrawalL2TransactionDto { level: number; transaction_hash: string; + log_index: number; amount: string; l2_token: { id: string; @@ -61,7 +67,8 @@ interface WithdrawalL2TransactionDto { level: number; index: number; commitment: { hash: string; } | null; - proof: string | null + proof: string | null; + cemented_at: string | null; } } diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/errors.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/errors.ts index dd63369..9a05872 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/errors.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/errors.ts @@ -1,7 +1,6 @@ import type { GraphQLError } from './dtos'; import { TokenBridgeError } from '../../common'; -import { EtherlinkToken } from '../../etherlink'; -import { TezosToken } from '../../tezos'; +import { TezosToken, EtherlinkToken } from '../../tokens'; import { tokenUtils } from '../../utils'; export class DipDupAutoUpdateIsDisabledError extends TokenBridgeError { @@ -25,3 +24,9 @@ export class DipDupTokenBalanceNotSupported extends TokenBridgeError { super(`DipDup won't be able to receive a balance of the ${tokenUtils.toDisplayString(token)}) token. Only ERC-20 tokens.`); } } + +export class DipDupTokenTransferIdInvalid extends TokenBridgeError { + constructor(tokenTransferId: string) { + super(`The token transfer Id is invalid: ${tokenTransferId}`); + } +} diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/mappers.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/mappers.ts index 97e1cf3..f0f57d3 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/mappers.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/mappers.ts @@ -5,14 +5,12 @@ import type { } from './dtos'; import { BridgeTokenTransferKind, BridgeTokenTransferStatus, - type BridgeTokenTransfer, - type BridgeTokenDeposit, type CreatedBridgeTokenDeposit, - type BridgeTokenWithdrawal, type CreatedBridgeTokenWithdrawal + type TezosTransferTokensOperation, type EtherlinkTransferTokensOperation, + type BridgeTokenTransfer, type BridgeTokenDeposit, type BridgeTokenWithdrawal } from '../../bridgeCore'; -import type { EtherlinkToken } from '../../etherlink'; import { getErrorLogMessage, loggerProvider } from '../../logging'; -import type { TezosToken } from '../../tezos'; -import { etherlinkUtils } from '../../utils'; +import type { TezosToken, EtherlinkToken } from '../../tokens'; +import { bridgeUtils, etherlinkUtils } from '../../utils'; import type { AccountTokenBalance, AccountTokenBalances } from '../balancesBridgeDataProvider'; const mapTezosTokenDtoToTezosToken = (tezosTokenDto: TezosTokenDto | undefined | null): TezosToken => { @@ -44,45 +42,60 @@ const mapEtherlinkTokenDtoToEtherlinkToken = (etherlinkTokenId: string | undefin }; }; -export const mapBridgeDepositDtoToDepositBridgeTokenTransfer = (dto: BridgeDepositDto): BridgeTokenDeposit | null => { +export const mapBridgeDepositDtoToDepositBridgeTokenTransfer = (dto: BridgeDepositDto, isFailed: boolean): BridgeTokenDeposit | null => { try { const source = dto.l1_transaction.l1_account; const receiver = etherlinkUtils.toChecksumAddress(dto.l1_transaction.l2_account); - const tezosOperation: CreatedBridgeTokenDeposit['tezosOperation'] = { + const tezosOperation: TezosTransferTokensOperation = { blockId: dto.l1_transaction.level, hash: dto.l1_transaction.operation_hash, + counter: dto.l1_transaction.counter, + nonce: dto.l1_transaction.nonce, amount: BigInt(dto.l1_transaction.amount), token: mapTezosTokenDtoToTezosToken(dto.l1_transaction.ticket.token), - // TODO: receive the fee - fee: 0n, timestamp: dto.l1_transaction.timestamp }; + const etherlinkOperation: EtherlinkTransferTokensOperation | undefined = dto.l2_transaction + ? { + blockId: dto.l2_transaction.level, + hash: etherlinkUtils.prepareHexPrefix(dto.l2_transaction.transaction_hash, true), + logIndex: dto.l2_transaction.log_index, + amount: BigInt(dto.l2_transaction.amount), + token: mapEtherlinkTokenDtoToEtherlinkToken(dto.l2_transaction.l2_token?.id), + timestamp: dto.l2_transaction.timestamp + } + : undefined; + const id = bridgeUtils.convertOperationDataToTokenTransferId(tezosOperation.hash, tezosOperation.counter, tezosOperation.nonce); - return dto.l2_transaction + return isFailed ? { + id, kind: BridgeTokenTransferKind.Deposit, - status: BridgeTokenTransferStatus.Finished, + status: BridgeTokenTransferStatus.Failed, source, receiver, tezosOperation, - etherlinkOperation: { - blockId: dto.l2_transaction.level, - hash: etherlinkUtils.prepareHexPrefix(dto.l2_transaction.transaction_hash, true), - amount: BigInt(dto.l2_transaction.amount), - token: mapEtherlinkTokenDtoToEtherlinkToken(dto.l2_transaction.l2_token?.id), - // TODO: receive the fee - fee: 0n, - timestamp: dto.l2_transaction.timestamp - } + etherlinkOperation } - : { - kind: BridgeTokenTransferKind.Deposit, - status: BridgeTokenTransferStatus.Created, - source, - receiver, - tezosOperation - }; + : etherlinkOperation + ? { + id, + kind: BridgeTokenTransferKind.Deposit, + status: BridgeTokenTransferStatus.Finished, + source, + receiver, + tezosOperation, + etherlinkOperation + } + : { + id, + kind: BridgeTokenTransferKind.Deposit, + status: BridgeTokenTransferStatus.Created, + source, + receiver, + tezosOperation + }; } catch (error) { loggerProvider.logger.error('Deposit DTO mapping error.', getErrorLogMessage(error)); @@ -90,70 +103,84 @@ export const mapBridgeDepositDtoToDepositBridgeTokenTransfer = (dto: BridgeDepos } }; -export const mapBridgeWithdrawalDtoToWithdrawalBridgeTokenTransfer = (dto: BridgeWithdrawalDto): BridgeTokenWithdrawal | null => { +export const mapBridgeWithdrawalDtoToWithdrawalBridgeTokenTransfer = (dto: BridgeWithdrawalDto, isFailed: boolean): BridgeTokenWithdrawal | null => { try { const source = etherlinkUtils.toChecksumAddress(dto.l2_transaction.l2_account); const receiver = dto.l2_transaction.l1_account; const amount = BigInt(dto.l2_transaction.amount); - const etherlinkOperation: CreatedBridgeTokenWithdrawal['etherlinkOperation'] = { + const etherlinkOperation: EtherlinkTransferTokensOperation = { blockId: dto.l2_transaction.level, hash: etherlinkUtils.prepareHexPrefix(dto.l2_transaction.transaction_hash, true), + logIndex: dto.l2_transaction.log_index, amount, token: mapEtherlinkTokenDtoToEtherlinkToken(dto.l2_transaction.l2_token?.id), - // TODO: receive the fee - fee: 0n, timestamp: dto.l2_transaction.timestamp }; + const tezosOperation: TezosTransferTokensOperation | undefined = dto.l1_transaction + ? { + blockId: dto.l1_transaction.level, + hash: dto.l1_transaction.operation_hash, + counter: dto.l1_transaction.counter, + nonce: dto.l1_transaction.nonce, + amount, + token: mapTezosTokenDtoToTezosToken(dto.l2_transaction.l2_token?.ticket?.token), + timestamp: dto.l1_transaction.timestamp + } + : undefined; + const rollupData = { + outboxMessageLevel: dto.l2_transaction.outbox_message.level, + outboxMessageIndex: dto.l2_transaction.outbox_message.index, + commitment: dto.l2_transaction.outbox_message.commitment?.hash || '', + proof: dto.l2_transaction.outbox_message.proof || '' + }; + const id = bridgeUtils.convertOperationDataToTokenTransferId(etherlinkOperation.hash, etherlinkOperation.logIndex); - return dto.l1_transaction + return isFailed ? { + id, kind: BridgeTokenTransferKind.Withdrawal, - status: BridgeTokenTransferStatus.Finished, + status: BridgeTokenTransferStatus.Failed, source, receiver, - tezosOperation: { - blockId: dto.l1_transaction.level, - hash: dto.l1_transaction.operation_hash, - amount, - token: mapTezosTokenDtoToTezosToken(dto.l2_transaction.l2_token?.ticket?.token), - // TODO: receive the fee - fee: 0n, - timestamp: dto.l1_transaction.timestamp - }, + tezosOperation, etherlinkOperation, - rollupData: { - outboxMessageLevel: dto.l2_transaction.outbox_message.level, - outboxMessageIndex: dto.l2_transaction.outbox_message.index, - commitment: dto.l2_transaction.outbox_message.commitment?.hash || '', - proof: dto.l2_transaction.outbox_message.proof || '' - } + rollupData } - : dto.l2_transaction.outbox_message.commitment && dto.l2_transaction.outbox_message.proof + : tezosOperation ? { + id, kind: BridgeTokenTransferKind.Withdrawal, - status: BridgeTokenTransferStatus.Sealed, + status: BridgeTokenTransferStatus.Finished, source, receiver, + tezosOperation, etherlinkOperation, - rollupData: { - outboxMessageLevel: dto.l2_transaction.outbox_message.level, - outboxMessageIndex: dto.l2_transaction.outbox_message.index, - commitment: dto.l2_transaction.outbox_message.commitment.hash, - proof: dto.l2_transaction.outbox_message.proof - } + rollupData } - : { - kind: BridgeTokenTransferKind.Withdrawal, - status: BridgeTokenTransferStatus.Created, - source, - receiver, - etherlinkOperation, - rollupData: { - outboxMessageLevel: dto.l2_transaction.outbox_message.level, - outboxMessageIndex: dto.l2_transaction.outbox_message.index + : rollupData.commitment && rollupData.proof + ? { + id, + kind: BridgeTokenTransferKind.Withdrawal, + status: BridgeTokenTransferStatus.Sealed, + source, + receiver, + etherlinkOperation, + rollupData } - }; + : { + id, + kind: BridgeTokenTransferKind.Withdrawal, + status: BridgeTokenTransferStatus.Created, + source, + receiver, + etherlinkOperation, + rollupData: { + outboxMessageLevel: dto.l2_transaction.outbox_message.level, + outboxMessageIndex: dto.l2_transaction.outbox_message.index, + estimatedOutboxMessageExecutionTimestamp: dto.l2_transaction.outbox_message.cemented_at || undefined + } + }; } catch (error) { loggerProvider.logger.error('Withdrawal DTO mapping error.', getErrorLogMessage(error)); @@ -162,9 +189,11 @@ export const mapBridgeWithdrawalDtoToWithdrawalBridgeTokenTransfer = (dto: Bridg }; export const mapBridgeOperationDtoToBridgeTokenTransfer = (dto: BridgeOperationDto): BridgeTokenTransfer | null => { + const isFailed = dto.is_completed && !dto.is_successful; + return dto.type === 'deposit' - ? mapBridgeDepositDtoToDepositBridgeTokenTransfer(dto.deposit!) - : mapBridgeWithdrawalDtoToWithdrawalBridgeTokenTransfer(dto.withdrawal!); + ? mapBridgeDepositDtoToDepositBridgeTokenTransfer(dto.deposit!, isFailed) + : mapBridgeWithdrawalDtoToWithdrawalBridgeTokenTransfer(dto.withdrawal!, isFailed); }; export const mapBridgeOperationsDtoToBridgeTokenTransfer = (dto: BridgeOperationsDto | BridgeOperationsStreamDto): BridgeTokenTransfer[] => { diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/webSocket/dipDupWebSocketClient.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/webSocket/dipDupWebSocketClient.ts index b9b539c..32e0b51 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/webSocket/dipDupWebSocketClient.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/webSocket/dipDupWebSocketClient.ts @@ -58,7 +58,7 @@ export class DipDupWebSocketClient { } stop() { - if (!this.isStarted) + if (!(this.isStarted || this._isStarting)) return; this.socket.events.messageReceived.removeListener(this.onSocketMessageReceived); @@ -67,6 +67,7 @@ export class DipDupWebSocketClient { this.reconnectScheduler[Symbol.dispose](); this._isStarted = false; + this._isStarting = false; } subscribe(query: string): boolean { diff --git a/src/bridgeDataProviders/dipDupBridgeDataProvider/webSocket/webSocketClient/index.node.ts b/src/bridgeDataProviders/dipDupBridgeDataProvider/webSocket/webSocketClient/index.node.ts index 9dc986b..c580d48 100644 --- a/src/bridgeDataProviders/dipDupBridgeDataProvider/webSocket/webSocketClient/index.node.ts +++ b/src/bridgeDataProviders/dipDupBridgeDataProvider/webSocket/webSocketClient/index.node.ts @@ -70,7 +70,7 @@ export class WebSocketClient implements WebSocketClientInterface { - throw new Error(`Websocket received error: ${JSON.stringify(event)}`); + throw new Error(`Websocket received error: ${event.message}`); }; protected onClosed = (event: WebSocketCloseEvent) => { diff --git a/src/bridgeDataProviders/index.ts b/src/bridgeDataProviders/index.ts index 28ca251..b9a2f94 100644 --- a/src/bridgeDataProviders/index.ts +++ b/src/bridgeDataProviders/index.ts @@ -1,6 +1,8 @@ export { + LocalTokensBridgeDataProvider, + type TokensBridgeDataProvider, - LocalTokensBridgeDataProvider + type TokensFetchOptions } from './tokensBridgeDataProvider'; export { @@ -10,11 +12,13 @@ export { type TokenBalanceInfo, type AccountTokenBalance, type AccountTokenBalances, - type BalancesBridgeDataProvider + type BalancesBridgeDataProvider, + type BalancesFetchOptions } from './balancesBridgeDataProvider'; export type { - TransfersBridgeDataProvider + TransfersBridgeDataProvider, + TransfersFetchOptions } from './transfersBridgeDataProvider'; export { diff --git a/src/bridgeDataProviders/tokensBridgeDataProvider/index.ts b/src/bridgeDataProviders/tokensBridgeDataProvider/index.ts index 29ecdc2..3686ee1 100644 --- a/src/bridgeDataProviders/tokensBridgeDataProvider/index.ts +++ b/src/bridgeDataProviders/tokensBridgeDataProvider/index.ts @@ -1,2 +1,4 @@ -export type { TokensBridgeDataProvider } from './tokensBridgeDataProvider'; export { LocalTokensBridgeDataProvider } from './localTokensBridgeDataProvider'; + +export type { TokensBridgeDataProvider } from './tokensBridgeDataProvider'; +export type { TokensFetchOptions } from './tokensFetchOptions'; diff --git a/src/bridgeDataProviders/tokensBridgeDataProvider/localTokensBridgeDataProvider.ts b/src/bridgeDataProviders/tokensBridgeDataProvider/localTokensBridgeDataProvider.ts index 0caf6c0..147c3aa 100644 --- a/src/bridgeDataProviders/tokensBridgeDataProvider/localTokensBridgeDataProvider.ts +++ b/src/bridgeDataProviders/tokensBridgeDataProvider/localTokensBridgeDataProvider.ts @@ -1,7 +1,7 @@ import type { TokensBridgeDataProvider } from './tokensBridgeDataProvider'; +import type { TokensFetchOptions } from './tokensFetchOptions'; import type { TokenPair } from '../../bridgeCore'; -import type { EtherlinkToken } from '../../etherlink'; -import type { TezosToken } from '../../tezos'; +import type { TezosToken, EtherlinkToken } from '../../tokens'; type TokenPairsByTokenMap = { native?: TokenPair, @@ -54,10 +54,16 @@ export class LocalTokensBridgeDataProvider implements TokensBridgeDataProvider { } getRegisteredTokenPairs(): Promise; - getRegisteredTokenPairs(offset: number, limit: number): Promise; - getRegisteredTokenPairs(offset?: number, limit?: number): Promise; - getRegisteredTokenPairs(offset?: number, limit?: number): Promise { - return Promise.resolve(this.tokenPairs.slice(offset, limit && (limit + (offset || 0)))); + getRegisteredTokenPairs(fetchOptions: TokensFetchOptions): Promise; + getRegisteredTokenPairs(fetchOptions?: TokensFetchOptions): Promise; + getRegisteredTokenPairs(fetchOptions?: TokensFetchOptions): Promise { + if (!fetchOptions) + return Promise.resolve(this.tokenPairs as TokenPair[]); + + const offset = fetchOptions.offset || 0; + const limit = fetchOptions.limit && (fetchOptions.limit + offset); + + return Promise.resolve(this.tokenPairs.slice(offset, limit)); } private createTokenPairsByTokenMap(tokenPairs: readonly TokenPair[]): TokenPairsByTokenMap { diff --git a/src/bridgeDataProviders/tokensBridgeDataProvider/tokensBridgeDataProvider.ts b/src/bridgeDataProviders/tokensBridgeDataProvider/tokensBridgeDataProvider.ts index 3dca2d5..0befccd 100644 --- a/src/bridgeDataProviders/tokensBridgeDataProvider/tokensBridgeDataProvider.ts +++ b/src/bridgeDataProviders/tokensBridgeDataProvider/tokensBridgeDataProvider.ts @@ -1,11 +1,11 @@ +import type { TokensFetchOptions } from './tokensFetchOptions'; import type { TokenPair } from '../../bridgeCore'; -import type { EtherlinkToken } from '../../etherlink'; -import type { TezosToken } from '../../tezos'; +import type { TezosToken, EtherlinkToken } from '../../tokens'; export interface TokensBridgeDataProvider { getRegisteredTokenPair(token: TezosToken | EtherlinkToken): Promise; getRegisteredTokenPairs(): Promise; - getRegisteredTokenPairs(offset: number, limit: number): Promise; - getRegisteredTokenPairs(offset?: number, limit?: number): Promise; + getRegisteredTokenPairs(fetchOptions: TokensFetchOptions): Promise; + getRegisteredTokenPairs(fetchOptions?: TokensFetchOptions): Promise; } diff --git a/src/bridgeDataProviders/tokensBridgeDataProvider/tokensFetchOptions.ts b/src/bridgeDataProviders/tokensBridgeDataProvider/tokensFetchOptions.ts new file mode 100644 index 0000000..28f9aca --- /dev/null +++ b/src/bridgeDataProviders/tokensBridgeDataProvider/tokensFetchOptions.ts @@ -0,0 +1,4 @@ +import type { FetchOptions } from '../../common'; + +export interface TokensFetchOptions extends FetchOptions { +} diff --git a/src/bridgeDataProviders/transfersBridgeDataProvider/index.ts b/src/bridgeDataProviders/transfersBridgeDataProvider/index.ts index 9b859a5..01abbf9 100644 --- a/src/bridgeDataProviders/transfersBridgeDataProvider/index.ts +++ b/src/bridgeDataProviders/transfersBridgeDataProvider/index.ts @@ -1 +1,2 @@ export type { TransfersBridgeDataProvider } from './transfersBridgeDataProvider'; +export type { TransfersFetchOptions } from './transfersFetchOptions'; diff --git a/src/bridgeDataProviders/transfersBridgeDataProvider/transfersBridgeDataProvider.ts b/src/bridgeDataProviders/transfersBridgeDataProvider/transfersBridgeDataProvider.ts index 1ca7eb2..3ad016f 100644 --- a/src/bridgeDataProviders/transfersBridgeDataProvider/transfersBridgeDataProvider.ts +++ b/src/bridgeDataProviders/transfersBridgeDataProvider/transfersBridgeDataProvider.ts @@ -1,3 +1,4 @@ +import type { TransfersFetchOptions } from './transfersFetchOptions'; import type { BridgeTokenTransfer, CreatedBridgeTokenDeposit, CreatedBridgeTokenWithdrawal } from '../../bridgeCore'; import type { PublicEventEmitter } from '../../common'; @@ -9,30 +10,25 @@ interface TransfersBridgeDataProviderEvents { export interface TransfersBridgeDataProvider { readonly events: TransfersBridgeDataProviderEvents; - getTokenTransfer(operationHash: string): Promise; - getTokenTransfer(tokenTransfer: BridgeTokenTransfer): Promise; - getTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise; + getTokenTransfer(tokenTransferId: string): Promise; getTokenTransfers(): Promise; - getTokenTransfers(offset: number, limit: number): Promise; - getTokenTransfers(offset?: number, limit?: number): Promise; + getTokenTransfers(fetchOptions: TransfersFetchOptions): Promise; + getTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise; getAccountTokenTransfers(accountAddress: string): Promise; getAccountTokenTransfers(accountAddresses: readonly string[]): Promise; - getAccountTokenTransfers(accountAddress: string, offset: number, limit: number): Promise; - getAccountTokenTransfers(accountAddresses: readonly string[], offset: number, limit: number): Promise; - getAccountTokenTransfers( - accountAddressOfAddresses: string | readonly string[], - offset?: number, - limit?: number - ): Promise; - - subscribeToTokenTransfer(operationHash: string): void; - subscribeToTokenTransfer(tokenTransfer: BridgeTokenTransfer): void; - subscribeToTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; - unsubscribeFromTokenTransfer(operationHash: string): void; - unsubscribeFromTokenTransfer(tokenTransfer: BridgeTokenTransfer): void; - unsubscribeFromTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + getAccountTokenTransfers(accountAddress: string, fetchOptions: TransfersFetchOptions): Promise; + getAccountTokenTransfers(accountAddresses: readonly string[], fetchOptions: TransfersFetchOptions): Promise; + getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], fetchOptions?: TransfersFetchOptions): Promise; + + getOperationTokenTransfers(operationHash: string): Promise; + getOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): Promise; + getOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise; + + subscribeToTokenTransfer(tokenTransferId: string): void; + + unsubscribeFromTokenTransfer(tokenTransferId: string): void; subscribeToTokenTransfers(): void; @@ -46,5 +42,13 @@ export interface TransfersBridgeDataProvider { unsubscribeFromAccountTokenTransfers(accountAddresses: readonly string[]): void; unsubscribeFromAccountTokenTransfers(accountAddressOrAddresses: string | readonly string[]): void; + subscribeToOperationTokenTransfers(operationHash: string): void; + subscribeToOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): void; + subscribeToOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + + unsubscribeFromOperationTokenTransfers(operationHash: string): void; + unsubscribeFromOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): void; + unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + unsubscribeFromAllSubscriptions(): void; } diff --git a/src/bridgeDataProviders/transfersBridgeDataProvider/transfersFetchOptions.ts b/src/bridgeDataProviders/transfersBridgeDataProvider/transfersFetchOptions.ts new file mode 100644 index 0000000..a3a3773 --- /dev/null +++ b/src/bridgeDataProviders/transfersBridgeDataProvider/transfersFetchOptions.ts @@ -0,0 +1,4 @@ +import type { FetchOptions } from '../../common'; + +export interface TransfersFetchOptions extends FetchOptions { +} diff --git a/src/common/errors.ts b/src/common/errors.ts index 265c35b..95f7bb8 100644 --- a/src/common/errors.ts +++ b/src/common/errors.ts @@ -8,7 +8,7 @@ export abstract class TokenBridgeError extends Error { } } -export class DisposedError extends Error { +export class DisposedError extends TokenBridgeError { } export class RemoteServiceResponseError extends TokenBridgeError { diff --git a/src/common/fetchOptions.ts b/src/common/fetchOptions.ts new file mode 100644 index 0000000..72cc1c4 --- /dev/null +++ b/src/common/fetchOptions.ts @@ -0,0 +1,4 @@ +export interface FetchOptions { + offset?: number; + limit?: number; +} diff --git a/src/common/index.ts b/src/common/index.ts index 5e491da..c7027ce 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -2,5 +2,4 @@ export { RemoteService, type RemoteServiceResponseFormat } from './remoteService export { EventEmitter, type PublicEventEmitter, type ToEventEmitter, type ToEventEmitters } from './eventEmitter'; export { TimeoutScheduler } from './timeoutScheduler'; export { TokenBridgeError, DisposedError } from './errors'; -export type { Network } from './network'; -export type { Token } from './token'; +export type { FetchOptions } from './fetchOptions'; diff --git a/src/common/network.ts b/src/common/network.ts deleted file mode 100644 index 669d435..0000000 --- a/src/common/network.ts +++ /dev/null @@ -1 +0,0 @@ -export type Network = 'mainnet' | 'testnet'; diff --git a/src/defaultTokenBridge.ts b/src/defaultTokenBridge.ts deleted file mode 100644 index c4b2830..0000000 --- a/src/defaultTokenBridge.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { TezosToolkit } from '@taquito/taquito'; -import type Web3 from 'web3'; - -import type { TokenPair } from './bridgeCore'; -import { DefaultDataProvider, type DefaultDataProviderOptions, type TokensBridgeDataProvider } from './bridgeDataProviders'; -import { LogLevel, loggerProvider, type Logger } from './logging'; -import { TokenBridge } from './tokenBridge/tokenBridge'; - -interface DefaultTezosTokenBridgeOptions { - toolkit: TezosToolkit; - rollupAddress: string; -} - -interface DefaultEtherlinkTokenBridgeOptions { - toolkit: Web3; -} - -interface LoggingOptions { - logger?: Logger - logLevel?: LogLevel -} - -export type DefaultTokenBridgeOptions = { - tezos: DefaultTezosTokenBridgeOptions; - etherlink: DefaultEtherlinkTokenBridgeOptions; - tokenPairs: TokensBridgeDataProvider | ReadonlyArray>; - logging?: LoggingOptions; -} & DefaultDataProviderOptions; - -export const defaultEtherlinkKernelAddress = '0x0000000000000000000000000000000000000000'; -export const defaultEtherlinkWithdrawPrecompileAddress = '0x0000000000000000000000000000000000000040'; - -export const createDefaultTokenBridge = (options: DefaultTokenBridgeOptions): TokenBridge => { - if (options.logging) { - options.logging.logger && loggerProvider.setLogger(options.logging.logger); - options.logging.logLevel && loggerProvider.setLogLevel(options.logging.logLevel); - } - loggerProvider.logger.debug('Creating the default token bridge...'); - - const defaultDataProvider = new DefaultDataProvider(options); - const tokenBridge = new TokenBridge({ - tezos: { - toolkit: options.tezos.toolkit, - bridgeOptions: { - rollupAddress: options.tezos.rollupAddress - } - }, - etherlink: { - toolkit: options.etherlink.toolkit, - bridgeOptions: { - kernelAddress: defaultEtherlinkKernelAddress, - withdrawPrecompileAddress: defaultEtherlinkWithdrawPrecompileAddress - } - }, - bridgeDataProviders: { - tokens: defaultDataProvider, - balances: defaultDataProvider, - transfers: defaultDataProvider - } - }); - - loggerProvider.logger.debug('The default token bridge has been created'); - - return tokenBridge; -}; diff --git a/src/etherlink/contracts/index.ts b/src/etherlink/contracts/index.ts deleted file mode 100644 index c963df7..0000000 --- a/src/etherlink/contracts/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { kernelContractAbi, type KernelContract } from './kernelContract'; diff --git a/src/etherlink/etherlinkBlockchainBridgeComponent.ts b/src/etherlink/etherlinkBlockchainBridgeComponent.ts deleted file mode 100644 index 3a4eb45..0000000 --- a/src/etherlink/etherlinkBlockchainBridgeComponent.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type Web3 from 'web3'; -// eslint-disable-next-line no-duplicate-imports -import type { TransactionReceipt } from 'web3'; - -import { kernelContractAbi, type KernelContract } from './contracts'; -import { tezosUtils } from '../utils'; - -interface WithdrawParams { - tezosReceiverAddress: string; - amount: bigint; - tezosTicketerContent: string; - etherlinkSenderAddress: string; - etherlinkTokenProxyContractAddress: string; - tezosTicketerAddress: string; - tezosProxyAddress: string; -} - -export interface EtherlinkBlockchainBridgeComponentOptions { - etherlinkToolkit: Web3; - kernelAddress: string; - withdrawPrecompileAddress: string; -} - -export class EtherlinkBlockchainBridgeComponent { - protected readonly etherlinkToolkit: Web3; - protected readonly kernelContract: KernelContract; - protected readonly withdrawPrecompileAddress: string; - - constructor(options: EtherlinkBlockchainBridgeComponentOptions) { - this.etherlinkToolkit = options.etherlinkToolkit; - this.withdrawPrecompileAddress = options.withdrawPrecompileAddress; - - this.kernelContract = new this.etherlinkToolkit.eth.Contract( - kernelContractAbi, - this.withdrawPrecompileAddress - ); - } - - async withdraw(params: WithdrawParams): Promise { - const tezosReceiverAddressBytes = tezosUtils.convertAddressToBytes(params.tezosReceiverAddress, true); - const tezosTicketerAddressBytes = tezosUtils.convertAddressToBytes(params.tezosTicketerAddress, true); - const tezosProxyAddressBytes = tezosUtils.convertAddressToBytes(params.tezosProxyAddress); - const receiverBytes = tezosReceiverAddressBytes + tezosProxyAddressBytes; - - const data = this.kernelContract.methods - .withdraw( - params.etherlinkTokenProxyContractAddress, - receiverBytes, - params.amount, - tezosTicketerAddressBytes, - params.tezosTicketerContent - ) - .encodeABI(); - - // TODO: refactoring - const gasPrice = await this.etherlinkToolkit.eth.getGasPrice(); - const receipt = await this.etherlinkToolkit.eth.sendTransaction({ - from: params.etherlinkSenderAddress, - to: this.withdrawPrecompileAddress, - gas: BigInt('30000'), // TODO: need to calculate the value or hardcode it in config - gasPrice, // without gasPrice we get the 'Network doesn't support eip-1559' exception - data, - }); - - return receipt; - } -} diff --git a/src/etherlink/index.ts b/src/etherlink/index.ts deleted file mode 100644 index 7691c13..0000000 --- a/src/etherlink/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { EtherlinkToken, NativeEtherlinkToken, NonNativeEtherlinkToken, ERC20EtherlinkToken } from './tokens'; -export { EtherlinkBlockchainBridgeComponent, type EtherlinkBlockchainBridgeComponentOptions } from './etherlinkBlockchainBridgeComponent'; diff --git a/src/index.ts b/src/index.ts index ba36b27..e0e888e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,16 +8,10 @@ export { type RemoteServiceResponseFormat, } from './common'; -export { - createDefaultTokenBridge, - defaultEtherlinkKernelAddress, - defaultEtherlinkWithdrawPrecompileAddress, - type DefaultTokenBridgeOptions -} from './defaultTokenBridge'; - export { TokenBridge, type TokenBridgeOptions, + type SignerTokenBalances } from './tokenBridge'; export { @@ -34,16 +28,21 @@ export { type TokenPair, + type TezosTransferTokensOperation, + type EtherlinkTransferTokensOperation, + type BridgeTokenTransfer, type BridgeTokenDeposit, type PendingBridgeTokenDeposit, type CreatedBridgeTokenDeposit, type FinishedBridgeTokenDeposit, + type FailedBridgeTokenDeposit, type BridgeTokenWithdrawal, type PendingBridgeTokenWithdrawal, type CreatedBridgeTokenWithdrawal, type SealedBridgeTokenWithdrawal, - type FinishedBridgeTokenWithdrawal + type FinishedBridgeTokenWithdrawal, + type FailedBridgeTokenWithdrawal } from './bridgeCore'; export type { @@ -51,15 +50,27 @@ export type { FA2TezosToken, NonNativeTezosToken, NativeTezosToken, - TezosToken -} from './tezos'; + TezosToken, -export type { ERC20EtherlinkToken, NonNativeEtherlinkToken, NativeEtherlinkToken, EtherlinkToken -} from './etherlink'; +} from './tokens'; + +export { + TaquitoContractTezosBridgeBlockchainService, + TaquitoWalletTezosBridgeBlockchainService, + Web3EtherlinkBridgeBlockchainService, + + type BridgeBlockchainService, + type TezosBridgeBlockchainService, + type EtherlinkBridgeBlockchainService, + + type TaquitoContractTezosBridgeBlockchainServiceOptions, + type TaquitoWalletTezosBridgeBlockchainServiceOptions, + type Web3EtherlinkBridgeBlockchainServiceOptions +} from './bridgeBlockchainService'; export { DipDupBridgeDataProvider, diff --git a/src/logging/logMessages.ts b/src/logging/logMessages.ts index 133a3de..813e7c8 100644 --- a/src/logging/logMessages.ts +++ b/src/logging/logMessages.ts @@ -18,6 +18,16 @@ export const getErrorLogMessage = (error: any): string => { export const getTokenLogMessage = tokenUtils.toDisplayString; +export const getBridgeTokenTransferIdLogMessage = (bridgeTokenTransfer: BridgeTokenTransfer | null | undefined): string => { + return bridgeTokenTransfer + ? bridgeTokenTransfer.status !== BridgeTokenTransferStatus.Pending + ? bridgeTokenTransfer.id + : `none (${bridgeUtils.getInitialOperation(bridgeTokenTransfer).hash})` + : bridgeTokenTransfer === null + ? 'null' + : 'undefined'; +}; + const getNullOrUndefinedBridgeTokenTransferLogMessage = (bridgeTokenTransfer: null | undefined): string => { return `Bridge Token transfer is ${bridgeTokenTransfer === null ? 'null' : 'undefined'}`; }; @@ -25,6 +35,7 @@ const getNullOrUndefinedBridgeTokenTransferLogMessage = (bridgeTokenTransfer: nu export const getBridgeTokenTransferLogMessage = (bridgeTokenTransfer: BridgeTokenTransfer | null | undefined): string => { return bridgeTokenTransfer ? `Bridge Token Transfer: + Id: ${bridgeTokenTransfer && bridgeTokenTransfer.status !== BridgeTokenTransferStatus.Pending ? bridgeTokenTransfer.id : 'none'} Kind: ${BridgeTokenTransferKind[bridgeTokenTransfer.kind]} Status: ${BridgeTokenTransferStatus[bridgeTokenTransfer.status]} Source: ${bridgeTokenTransfer.source} @@ -38,7 +49,7 @@ export const getBridgeTokenTransferLogMessage = (bridgeTokenTransfer: BridgeToke export const getDetailedBridgeTokenTransferLogMessage = (bridgeTokenTransfer: BridgeTokenTransfer | null | undefined): string => { return bridgeTokenTransfer - ? `Bridge Token Transfer [${bridgeUtils.getInitialOperationHash(bridgeTokenTransfer)}]: + ? `Bridge Token Transfer [${getBridgeTokenTransferIdLogMessage(bridgeTokenTransfer)}]: ${bridgeUtils.stringifyBridgeTokenTransfer(bridgeTokenTransfer, 2)} diff --git a/src/tezos/common/index.ts b/src/tezos/common/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/tezos/index.ts b/src/tezos/index.ts deleted file mode 100644 index 1b6ca6b..0000000 --- a/src/tezos/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type { TezosToken, NativeTezosToken, NonNativeTezosToken, FA12TezosToken, FA2TezosToken } from './tokens'; -export { TezosBlockchainBridgeComponent } from './tezosBlockchainBridgeComponent'; -export type { TezosBlockchainBridgeComponentOptions } from './tezosBlockchainBridgeComponentOptions'; -export { tezosTicketerContentMichelsonType } from './tezosTicketerContentMichelsonType'; diff --git a/src/tezos/tezosBlockchainBridgeComponent.ts b/src/tezos/tezosBlockchainBridgeComponent.ts deleted file mode 100644 index d607bdf..0000000 --- a/src/tezos/tezosBlockchainBridgeComponent.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { ContractProvider, OperationBatch, ParamsWithKind, Wallet, WalletOperationBatch, WalletParamsWithKind } from '@taquito/taquito'; - -import type { NonNativeTokenTicketHelper, FA12Contract, FA2Contract, NativeTokenTicketHelper } from './contracts'; -import { TezosBlockchainBridgeComponentBase } from './tezosBlockchainBridgeComponentBase'; -import type { TezosBlockchainBridgeComponentOptions } from './tezosBlockchainBridgeComponentOptions'; - -export class TezosBlockchainBridgeComponent extends TezosBlockchainBridgeComponentBase { - readonly wallet: WalletTezosBlockchainBridgeComponent; - - constructor(options: TezosBlockchainBridgeComponentOptions) { - super(options); - - this.wallet = new WalletTezosBlockchainBridgeComponent(options); - } - - protected createBatch(params?: ParamsWithKind[]): OperationBatch { - return this.tezosToolkit.contract.batch(params); - } - - protected getNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise> { - return this.tezosToolkit.contract.at>(ticketHelperContractAddress); - } - - protected getNonNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise> { - return this.tezosToolkit.contract.at>(ticketHelperContractAddress); - } - - protected getFA12TokenContract(fa12TokenContractAddress: string): Promise> { - return this.tezosToolkit.contract.at>(fa12TokenContractAddress); - } - - protected getFA2TokenContract(fa2TokenContractAddress: string): Promise> { - return this.tezosToolkit.contract.at>(fa2TokenContractAddress); - } -} - -export class WalletTezosBlockchainBridgeComponent extends TezosBlockchainBridgeComponentBase { - protected createBatch(params?: WalletParamsWithKind[]): WalletOperationBatch { - return this.tezosToolkit.wallet.batch(params); - } - - protected getNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise> { - return this.tezosToolkit.wallet.at>(ticketHelperContractAddress); - } - - protected getNonNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise> { - return this.tezosToolkit.wallet.at>(ticketHelperContractAddress); - } - - protected getFA12TokenContract(fa12TokenContractAddress: string): Promise> { - return this.tezosToolkit.wallet.at>(fa12TokenContractAddress); - } - - protected getFA2TokenContract(fa2TokenContractAddress: string): Promise> { - return this.tezosToolkit.wallet.at>(fa2TokenContractAddress); - } -} diff --git a/src/tezos/tezosBlockchainBridgeComponentBase.ts b/src/tezos/tezosBlockchainBridgeComponentBase.ts deleted file mode 100644 index 20ba48e..0000000 --- a/src/tezos/tezosBlockchainBridgeComponentBase.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { LocalForger } from '@taquito/local-forging'; -import { type TezosToolkit, type Wallet, type ContractProvider, type ContractMethod } from '@taquito/taquito'; - -import type { FA12Contract, FA2Contract, NativeTokenTicketHelper, NonNativeTokenTicketHelper } from './contracts'; -import { fa12helper, fa2helper } from './helpers'; -import type { TezosBlockchainBridgeComponentOptions } from './tezosBlockchainBridgeComponentOptions'; -import type { FA12TezosToken, FA2TezosToken, NonNativeTezosToken } from './tokens'; - -export interface DepositNativeTokenParams { - amount: bigint; - etherlinkReceiverAddress: string; - ticketHelperContractAddress: string; -} - -export interface DepositNonNativeTokensParams { - token: NonNativeTezosToken; - amount: bigint; - etherlinkReceiverAddress: string; - ticketHelperContractAddress: string; -} - -export abstract class TezosBlockchainBridgeComponentBase { - protected readonly tezosToolkit: TezosToolkit; - protected readonly rollupAddress: string; - protected readonly localForger = new LocalForger(); - - constructor(options: TezosBlockchainBridgeComponentOptions) { - this.tezosToolkit = options.tezosToolkit; - this.rollupAddress = options.rollupAddress; - } - - async depositNativeToken(params: DepositNativeTokenParams): Promise['send']>> { - const depositOperation = await this.createDepositNativeTokenOperation( - params.etherlinkReceiverAddress, - params.ticketHelperContractAddress - ); - - const batch = this.createBatch() - .withContractCall(depositOperation as ContractMethod & ContractMethod, { amount: Number(params.amount), mutez: true }); - - return batch.send() as ReturnType['send']>; - } - - async depositNonNativeToken(params: DepositNonNativeTokensParams): Promise['send']>> { - const depositOperation = await this.createDepositNonNativeTokenOperation( - params.etherlinkReceiverAddress, - params.amount, - params.ticketHelperContractAddress - ); - - let batch = this.createBatch(); - batch = await (params.token.type === 'fa2' - ? this.wrapContractCallsWithFA2TokenApprove(batch, depositOperation, params.token, params.ticketHelperContractAddress) - : this.wrapContractCallsWithFA12TokenApprove(batch, depositOperation, params.token, params.amount, params.ticketHelperContractAddress)); - - return batch.send() as ReturnType['send']>; - } - - async finishWithdraw(commitment: string, proof: string) { - return this.tezosToolkit.contract.smartRollupExecuteOutboxMessage({ - rollup: this.rollupAddress, - cementedCommitment: commitment, - outputProof: proof - }); - } - - protected async createDepositNativeTokenOperation(etherlinkReceiverAddress: string, ticketerContractAddress: string) { - const ticketHelperContract = await this.getNativeTokenTicketHelperContract(ticketerContractAddress); - const routingInfo = this.packDepositRoutingInfo(etherlinkReceiverAddress); - - const operation = ticketHelperContract.methodsObject.deposit({ - evm_address: this.rollupAddress, - l2_address: routingInfo, - }); - - return operation; - } - - protected async createDepositNonNativeTokenOperation( - etherlinkReceiverAddress: string, - amount: bigint, - ticketHelperContractAddress: string - ) { - const ticketHelperContract = await this.getNonNativeTokenTicketHelperContract(ticketHelperContractAddress); - const routingInfo = this.packDepositRoutingInfo(etherlinkReceiverAddress); - - const operation = ticketHelperContract.methodsObject.deposit({ - rollup: this.rollupAddress, - receiver: routingInfo, - amount - }); - - return operation; - } - - protected packDepositRoutingInfo(etherlinkReceiverAddress: string, etherlinkProxyAddress?: string): string { - // TODO: validate - // checkEvmAddressIsCorrect(etherlinkReceiverAddress); - // checkEvmAddressIsCorrect(etherlinkTokenProxyContractAddress); - return (etherlinkProxyAddress) - ? `${etherlinkReceiverAddress.substring(2)}${etherlinkProxyAddress.substring(2)}` - : etherlinkReceiverAddress.substring(2); - } - - protected async wrapContractCallsWithFA12TokenApprove( - batch: ReturnType, - contractCalls: Parameters[0]['contractCalls'], - token: FA12TezosToken, - amount: bigint, - ticketHelperContractAddress: string - ): Promise> { - const tokenContract = await this.getFA12TokenContract(token.address); - const resultOperation = fa12helper.wrapContractCallsWithApprove({ - batch, - approvedAddress: ticketHelperContractAddress, - approvedAmount: amount, - tokenContract, - contractCalls - }); - - return resultOperation as ReturnType; - } - - protected async wrapContractCallsWithFA2TokenApprove( - batch: ReturnType, - contractCalls: Parameters[0]['contractCalls'], - token: FA2TezosToken, - ticketHelperContractAddress: string - ): Promise> { - const [tokenContract, tokenOwnerAddress] = await Promise.all([ - this.getFA2TokenContract(token.address), - this.tezosToolkit.wallet.pkh() - ]); - - const resultOperation = fa2helper.wrapContractCallsWithApprove({ - batch, - approvedAddress: ticketHelperContractAddress, - ownerAddress: tokenOwnerAddress, - tokenId: token.tokenId, - tokenContract, - contractCalls - }); - - return resultOperation as ReturnType; - } - - protected abstract createBatch(params?: Parameters[0]): ReturnType; - protected abstract getNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise>; - protected abstract getNonNativeTokenTicketHelperContract(ticketHelperContractAddress: string): Promise>; - protected abstract getFA12TokenContract(fa12TokenContractAddress: string): Promise>; - protected abstract getFA2TokenContract(fa2TokenContractAddress: string): Promise>; -} diff --git a/src/tezos/tezosBlockchainBridgeComponentOptions.ts b/src/tezos/tezosBlockchainBridgeComponentOptions.ts deleted file mode 100644 index 5aa19bb..0000000 --- a/src/tezos/tezosBlockchainBridgeComponentOptions.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { TezosToolkit } from '@taquito/taquito'; - -export interface TezosBlockchainBridgeComponentOptions { - tezosToolkit: TezosToolkit; - rollupAddress: string; -} diff --git a/src/tokenBridge/errors.ts b/src/tokenBridge/errors.ts index 4ce6ddd..3fe3e08 100644 --- a/src/tokenBridge/errors.ts +++ b/src/tokenBridge/errors.ts @@ -1,8 +1,15 @@ -import { TokenBridgeError } from '../common'; -import type { EtherlinkToken } from '../etherlink'; -import type { TezosToken } from '../tezos'; +import type { BridgeTokenTransfer } from '../bridgeCore'; +import { DisposedError, TokenBridgeError } from '../common'; +import { getBridgeTokenTransferIdLogMessage } from '../logging'; +import type { TezosToken, EtherlinkToken } from '../tokens'; import { tokenUtils } from '../utils'; +export class TokenBridgeDisposed extends DisposedError { + constructor() { + super('The TokenBridge is disposed.'); + } +} + export class TokenPairNotFoundError extends TokenBridgeError { constructor(token: TezosToken | EtherlinkToken) { super(TokenPairNotFoundError.getMessage(token)); @@ -22,3 +29,25 @@ export class InsufficientBalanceError extends TokenBridgeError { return `${address} has an insufficient balance ${tokenUtils.toDisplayString(token)}. Balance = ${balance}. Requested = ${requested}`; } } + +export class FailedTokenTransferError extends TokenBridgeError { + constructor(tokenTransfer: BridgeTokenTransfer) { + super(FailedTokenTransferError.getMessage(tokenTransfer)); + } + + private static getMessage(tokenTransfer: BridgeTokenTransfer): string { + return `The ${getBridgeTokenTransferIdLogMessage(tokenTransfer)} token transfer is failed`; + } +} + +export class TezosSignerAccountUnavailableError extends TokenBridgeError { + constructor() { + super('The Tezos signer account is unavailable'); + } +} + +export class EtherlinkSignerAccountUnavailableError extends TokenBridgeError { + constructor() { + super('The Etherlink signer account is unavailable'); + } +} diff --git a/src/tokenBridge/index.ts b/src/tokenBridge/index.ts index a1a6a5e..bd5c8fc 100644 --- a/src/tokenBridge/index.ts +++ b/src/tokenBridge/index.ts @@ -1,2 +1,4 @@ export { TokenBridge } from './tokenBridge'; export type { TokenBridgeOptions } from './tokenBridgeOptions'; + +export type { SignerTokenBalances } from './tokenBridgeDataApi'; diff --git a/src/tokenBridge/tokenBridge.ts b/src/tokenBridge/tokenBridge.ts index 3efa8b5..3e68b70 100644 --- a/src/tokenBridge/tokenBridge.ts +++ b/src/tokenBridge/tokenBridge.ts @@ -1,59 +1,55 @@ -import { packDataBytes } from '@taquito/michel-codec'; -import { Schema } from '@taquito/michelson-encoder'; -import type { TezosToolkit } from '@taquito/taquito'; -import type Web3 from 'web3'; - -import { InsufficientBalanceError, TokenPairNotFoundError } from './errors'; +import { EtherlinkSignerAccountUnavailableError, FailedTokenTransferError, InsufficientBalanceError, TezosSignerAccountUnavailableError, TokenBridgeDisposed, TokenPairNotFoundError } from './errors'; import type { TokenBridgeComponents } from './tokenBridgeComponents'; -import type { TokenBridgeDataApi } from './tokenBridgeDataApi'; +import type { SignerTokenBalances, TokenBridgeDataApi } from './tokenBridgeDataApi'; import type { TokenBridgeOptions } from './tokenBridgeOptions'; import type { TokenBridgeStreamApi } from './tokenBridgeStreamApi'; +import type { EtherlinkBridgeBlockchainService, TezosBridgeBlockchainService } from '../bridgeBlockchainService'; import { BridgeTokenTransferKind, BridgeTokenTransferStatus, type TokenPair, - type DepositOptions, type WalletDepositOptions, type DepositResult, type WalletDepositResult, - type StartWithdrawResult, type FinishWithdrawResult, + type DepositOptions, type DepositResult, type StartWithdrawResult, type FinishWithdrawResult, type BridgeTokenTransfer, type PendingBridgeTokenDeposit, type CreatedBridgeTokenDeposit, type FinishedBridgeTokenDeposit, type PendingBridgeTokenWithdrawal, type CreatedBridgeTokenWithdrawal, type SealedBridgeTokenWithdrawal, type FinishedBridgeTokenWithdrawal } from '../bridgeCore'; -import type { AccountTokenBalance, AccountTokenBalances } from '../bridgeDataProviders'; -import { EventEmitter, ToEventEmitter, type PublicEventEmitter, DisposedError } from '../common'; -import { EtherlinkBlockchainBridgeComponent, EtherlinkToken, type NonNativeEtherlinkToken } from '../etherlink'; +import type { + AccountTokenBalance, AccountTokenBalances, + BalancesFetchOptions, TokensFetchOptions, TransfersFetchOptions +} from '../bridgeDataProviders'; +import { EventEmitter, ToEventEmitter, type PublicEventEmitter } from '../common'; import { loggerProvider, getTokenLogMessage, getBridgeTokenTransferLogMessage, getDetailedBridgeTokenTransferLogMessage, getErrorLogMessage } from '../logging'; -import { TezosBlockchainBridgeComponent, tezosTicketerContentMichelsonType, type TezosToken } from '../tezos'; -import { bridgeUtils, etherlinkUtils, guards } from '../utils'; - -type TokenTransferCreatedEventArgument = readonly [ - tokenTransfer: PendingBridgeTokenDeposit | PendingBridgeTokenWithdrawal | CreatedBridgeTokenDeposit | CreatedBridgeTokenWithdrawal -]; +import type { + TezosToken, NativeTezosToken, NonNativeTezosToken, + EtherlinkToken, NativeEtherlinkToken, NonNativeEtherlinkToken +} from '../tokens'; +import { bridgeUtils, guards } from '../utils'; interface TokenBridgeComponentsEvents { - readonly tokenTransferCreated: PublicEventEmitter; + readonly tokenTransferCreated: PublicEventEmitter; readonly tokenTransferUpdated: PublicEventEmitter; } -export class TokenBridge implements Disposable { +export class TokenBridge< + TTezosBridgeBlockchainService extends TezosBridgeBlockchainService = TezosBridgeBlockchainService, + TEtherlinkBridgeBlockchainService extends EtherlinkBridgeBlockchainService = EtherlinkBridgeBlockchainService +> implements Disposable { private static readonly defaultLastCreatedTokenTransfersTimerPeriod = 60_000; private static readonly defaultLastCreatedTokenTransferLifetime = 30_000; readonly data: TokenBridgeDataApi; readonly stream: TokenBridgeStreamApi; - readonly bridgeComponents: TokenBridgeComponents; + readonly bridgeComponents: TokenBridgeComponents; protected readonly events: TokenBridgeComponentsEvents = { tokenTransferCreated: new EventEmitter(), tokenTransferUpdated: new EventEmitter() }; - protected readonly tezosToolkit: TezosToolkit; - protected readonly etherlinkToolkit: Web3; - protected readonly tezosTicketerContentSchema = new Schema(tezosTicketerContentMichelsonType); - protected readonly tokenTransferOperationWatchers = new Map< + protected readonly tokenTransferStatusWatchers = new Map< string, Map, @@ -66,22 +62,10 @@ export class TokenBridge implements Disposable { private lastCreatedTokenTransfersTimerId: ReturnType | undefined; private _isDisposed = false; - constructor(options: TokenBridgeOptions) { - this.tezosToolkit = options.tezos.toolkit; - this.etherlinkToolkit = options.etherlink.toolkit; - - const tezosBlockchainBridgeComponent = new TezosBlockchainBridgeComponent({ - tezosToolkit: this.tezosToolkit, - rollupAddress: options.tezos.bridgeOptions.rollupAddress - }); - const etherlinkBlockchainBridgeComponent = new EtherlinkBlockchainBridgeComponent({ - etherlinkToolkit: this.etherlinkToolkit, - kernelAddress: options.etherlink.bridgeOptions.kernelAddress, - withdrawPrecompileAddress: options.etherlink.bridgeOptions.withdrawPrecompileAddress - }); + constructor(options: TokenBridgeOptions) { this.bridgeComponents = { - tezos: tezosBlockchainBridgeComponent, - etherlink: etherlinkBlockchainBridgeComponent, + tezosBridgeBlockchainService: options.tezosBridgeBlockchainService, + etherlinkBridgeBlockchainService: options.etherlinkBridgeBlockchainService, tokensBridgeDataProvider: options.bridgeDataProviders.tokens, balancesBridgeDataProvider: options.bridgeDataProviders.balances, transfersBridgeDataProvider: options.bridgeDataProviders.transfers @@ -94,14 +78,19 @@ export class TokenBridge implements Disposable { getTokenTransfer: this.getTokenTransfer.bind(this), getTokenTransfers: this.getTokenTransfers.bind(this), getAccountTokenTransfers: this.getAccountTokenTransfers.bind(this), + getOperationTokenTransfers: this.getOperationTokenTransfers.bind(this), + getSignerBalances: this.getSignerBalances.bind(this), + getSignerTokenTransfers: this.getSignerTokenTransfers.bind(this), }; this.stream = { subscribeToTokenTransfer: this.subscribeToTokenTransfer.bind(this), subscribeToTokenTransfers: this.subscribeToTokenTransfers.bind(this), subscribeToAccountTokenTransfers: this.subscribeToAccountTokenTransfers.bind(this), + subscribeToOperationTokenTransfers: this.subscribeToOperationTokenTransfers.bind(this), unsubscribeFromTokenTransfer: this.unsubscribeFromTokenTransfer.bind(this), unsubscribeFromTokenTransfers: this.unsubscribeFromTokenTransfers.bind(this), unsubscribeFromAccountTokenTransfers: this.unsubscribeFromAccountTokenTransfers.bind(this), + unsubscribeFromOperationTokenTransfers: this.unsubscribeFromOperationTokenTransfers.bind(this), unsubscribeFromAllSubscriptions: this.unsubscribeFromAllSubscriptions.bind(this) }; @@ -147,15 +136,20 @@ export class TokenBridge implements Disposable { if (transfer.status >= status) return transfer; - const operationHash = bridgeUtils.getInitialOperationHash(transfer); - const updatedTransfer = await this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfer(operationHash); + const isPending = transfer.status === BridgeTokenTransferStatus.Pending; + const updatedTransfers = await (isPending + ? this.bridgeComponents.transfersBridgeDataProvider.getOperationTokenTransfers(transfer) + : this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfer(transfer.id)); + const updatedTransfer = guards.isArray(updatedTransfers) ? updatedTransfers[0] : updatedTransfers; + if (updatedTransfer?.status === status) return updatedTransfer; - let statusWatchers = this.tokenTransferOperationWatchers.get(operationHash); + const statusWatcherKey = isPending ? bridgeUtils.getInitialOperation(transfer).hash : transfer.id; + let statusWatchers = this.tokenTransferStatusWatchers.get(statusWatcherKey); if (!statusWatchers) { statusWatchers = new Map(); - this.tokenTransferOperationWatchers.set(operationHash, statusWatchers); + this.tokenTransferStatusWatchers.set(statusWatcherKey, statusWatchers); } const watcher = statusWatchers.get(status); @@ -163,23 +157,24 @@ export class TokenBridge implements Disposable { return watcher.promise; const watcherPromise = new Promise((resolve, reject) => { - const statusWatchers = this.tokenTransferOperationWatchers.get(operationHash); + const statusWatchers = this.tokenTransferStatusWatchers.get(statusWatcherKey); if (!statusWatchers) { - reject(`Status watchers map not found for the ${operationHash} operation`); + reject(`Status watchers map not found for the ${statusWatcherKey} token transfer`); return; } setTimeout(() => { try { - this.bridgeComponents.transfersBridgeDataProvider.subscribeToTokenTransfer(operationHash); + this.bridgeComponents.transfersBridgeDataProvider[isPending ? 'subscribeToOperationTokenTransfers' : 'subscribeToTokenTransfer'](statusWatcherKey); + statusWatchers.set(status, { promise: watcherPromise, resolve: (updatedTransfer: BridgeTokenTransfer) => { - this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromTokenTransfer(operationHash); + this.bridgeComponents.transfersBridgeDataProvider[isPending ? 'unsubscribeFromOperationTokenTransfers' : 'unsubscribeFromTokenTransfer'](statusWatcherKey); resolve(updatedTransfer); }, reject: (error: unknown) => { - this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromTokenTransfer(operationHash); + this.bridgeComponents.transfersBridgeDataProvider[isPending ? 'unsubscribeFromOperationTokenTransfers' : 'unsubscribeFromTokenTransfer'](statusWatcherKey); reject(error); } }); @@ -194,30 +189,37 @@ export class TokenBridge implements Disposable { return watcherPromise; } - async deposit(amount: bigint, token: TezosToken): Promise; - async deposit(amount: bigint, token: TezosToken, etherlinkReceiverAddress: string): Promise; - async deposit(amount: bigint, token: TezosToken, options: WalletDepositOptions): Promise; - async deposit(amount: bigint, token: TezosToken, options: DepositOptions): Promise; - async deposit(amount: bigint, token: TezosToken, etherlinkReceiverAddress: string, options: WalletDepositOptions): Promise; - async deposit(amount: bigint, token: TezosToken, etherlinkReceiverAddress: string, options: DepositOptions): Promise; + async deposit(amount: bigint, token: NativeTezosToken): Promise>; + async deposit(amount: bigint, token: NativeTezosToken, etherlinkReceiverAddress: string): Promise>; + async deposit(amount: bigint, token: NativeTezosToken, options: DepositOptions): Promise>; + async deposit(amount: bigint, token: NativeTezosToken, etherlinkReceiverAddress: string, options: DepositOptions): Promise>; + async deposit(amount: bigint, token: NonNativeTezosToken): Promise>; + async deposit(amount: bigint, token: NonNativeTezosToken, etherlinkReceiverAddress: string): Promise>; + async deposit(amount: bigint, token: NonNativeTezosToken, options: DepositOptions): Promise>; + async deposit(amount: bigint, token: NonNativeTezosToken, etherlinkReceiverAddress: string, options: DepositOptions): Promise>; + async deposit( + amount: bigint, + token: TezosToken, + etherlinkReceiverAddressOrOptions?: string | DepositOptions, + options?: DepositOptions + ): Promise | DepositResult>; async deposit( amount: bigint, token: TezosToken, - etherlinkReceiverAddressOrOptions?: string | WalletDepositOptions | DepositOptions, - options?: WalletDepositOptions | DepositOptions - ): Promise { + etherlinkReceiverAddressOrOptions?: string | DepositOptions, + options?: DepositOptions + ): Promise | DepositResult> { this.ensureIsNotDisposed(); - const tezosSourceAddress = await this.getTezosConnectedAddress(); + + const tezosSourceAddress = await this.getRequiredTezosSignerAddress(); const etherlinkReceiverAddress = typeof etherlinkReceiverAddressOrOptions === 'string' ? etherlinkReceiverAddressOrOptions - : await this.getEtherlinkConnectedAddress(); + : await this.getRequiredEtherlinkSignerAddress(); const depositOptions = typeof etherlinkReceiverAddressOrOptions !== 'string' && etherlinkReceiverAddressOrOptions ? etherlinkReceiverAddressOrOptions : options; - const useWalletApi = depositOptions && depositOptions.useWalletApi !== undefined ? depositOptions.useWalletApi : true; loggerProvider.lazyLogger.log?.( `Depositing ${amount.toString(10)} ${getTokenLogMessage(token)} from ${tezosSourceAddress} to ${etherlinkReceiverAddress}` ); - loggerProvider.logger.debug(`Use the Taquito ${useWalletApi ? 'Wallet' : 'Contract'} API`); const tokenPair = await this.bridgeComponents.tokensBridgeDataProvider.getRegisteredTokenPair(token); if (!tokenPair) { @@ -233,38 +235,72 @@ export class TokenBridge implements Disposable { } loggerProvider.logger.log(`The ${tezosSourceAddress} has enough tokens to deposit ${amount}`); - const ticketHelperContractAddress = tokenPair.tezos.ticketHelperContractAddress; - - const depositOperation = await (useWalletApi - ? this.depositUsingWalletApi( - token, + const operationResult = await (token.type === 'native' + ? this.bridgeComponents.tezosBridgeBlockchainService.depositNativeToken({ amount, etherlinkReceiverAddress, - depositOptions as WalletDepositOptions, - ticketHelperContractAddress - ) - : this.depositUsingContractApi( + ticketHelperContractAddress: tokenPair.tezos.ticketHelperContractAddress, + }) + : this.bridgeComponents.tezosBridgeBlockchainService.depositNonNativeToken({ token, amount, etherlinkReceiverAddress, - depositOptions as DepositOptions, - ticketHelperContractAddress - ) + ticketHelperContractAddress: tokenPair.tezos.ticketHelperContractAddress, + useApprove: depositOptions?.useApprove, + resetFA12Approve: depositOptions?.resetFA12Approve, + }) ); + loggerProvider.logger.log('The deposit operation has been created:', operationResult.hash); + + const tokenTransfer: PendingBridgeTokenDeposit = { + kind: BridgeTokenTransferKind.Deposit, + status: BridgeTokenTransferStatus.Pending, + source: tezosSourceAddress, + receiver: etherlinkReceiverAddress, + tezosOperation: { + hash: operationResult.hash, + amount: operationResult.amount, + timestamp: operationResult.timestamp, + token, + } + }; - loggerProvider.lazyLogger.log?.(getBridgeTokenTransferLogMessage(depositOperation.tokenTransfer)); - loggerProvider.lazyLogger.debug?.(getDetailedBridgeTokenTransferLogMessage(depositOperation.tokenTransfer)); + loggerProvider.lazyLogger.log?.(getBridgeTokenTransferLogMessage(tokenTransfer)); + loggerProvider.lazyLogger.debug?.(getDetailedBridgeTokenTransferLogMessage(tokenTransfer)); - this.emitLocalTokenTransferCreatedEvent(depositOperation.tokenTransfer); + this.emitLocalTokenTransferCreatedEvent(tokenTransfer); - return depositOperation; + return { + tokenTransfer, + operationResult + } as DepositResult | DepositResult; } - async startWithdraw(amount: bigint, token: NonNativeEtherlinkToken, tezosReceiverAddress?: string): Promise { + async startWithdraw( + amount: bigint, + token: NativeEtherlinkToken, + tezosReceiverAddress?: string + ): Promise>; + async startWithdraw( + amount: bigint, + token: NonNativeEtherlinkToken, + tezosReceiverAddress?: string + ): Promise>; + async startWithdraw( + amount: bigint, + token: EtherlinkToken, + tezosReceiverAddress?: string + ): Promise | StartWithdrawResult>; + async startWithdraw( + amount: bigint, + token: EtherlinkToken, + tezosReceiverAddress?: string + ): Promise | StartWithdrawResult> { this.ensureIsNotDisposed(); - const etherlinkSourceAddress = await this.getEtherlinkConnectedAddress(); + + const etherlinkSourceAddress = await this.getRequiredEtherlinkSignerAddress(); if (!tezosReceiverAddress) { - tezosReceiverAddress = await this.getTezosConnectedAddress(); + tezosReceiverAddress = await this.getRequiredTezosSignerAddress(); } const tokenPair = await this.bridgeComponents.tokensBridgeDataProvider.getRegisteredTokenPair(token); @@ -273,8 +309,6 @@ export class TokenBridge implements Disposable { loggerProvider.logger.error(getErrorLogMessage(error)); throw error; } - if (tokenPair.etherlink.type === 'native' || tokenPair.tezos.type === 'native') - throw new Error('Withdrawal of native tokens is not supported yet'); const accountTokenBalance = await this.bridgeComponents.balancesBridgeDataProvider.getBalance(etherlinkSourceAddress, token); if (amount > accountTokenBalance.balance) { const error = new InsufficientBalanceError(token, etherlinkSourceAddress, accountTokenBalance.balance, amount); @@ -283,33 +317,35 @@ export class TokenBridge implements Disposable { } loggerProvider.logger.log(`The ${etherlinkSourceAddress} has enough tokens to withdraw ${amount}`); - const tezosTicketerAddress = tokenPair.tezos.ticketerContractAddress; - const etherlinkTokenProxyContractAddress = tokenPair.etherlink.address; - const tezosProxyAddress = tezosTicketerAddress; + let operationResult: Awaited>['operationResult']; + if (tokenPair.tezos.type === 'native') { + operationResult = (await this.bridgeComponents.etherlinkBridgeBlockchainService.withdrawNativeToken({ + amount, + tezosReceiverAddress, + }) as Awaited>['operationResult']); + } + else { + const tezosTicketerAddress = tokenPair.tezos.ticketerContractAddress; + const tezosTicketerContent = await this.bridgeComponents.tezosBridgeBlockchainService.getTezosTicketerContent(tezosTicketerAddress); - const [etherlinkConnectedAddress, tezosTicketerContent] = await Promise.all([ - this.getEtherlinkConnectedAddress(), - this.getTezosTicketerContent(tezosTicketerAddress) - ]); + operationResult = (await this.bridgeComponents.etherlinkBridgeBlockchainService.withdrawNonNativeToken({ + amount, + tezosReceiverAddress, + tezosTicketerAddress, + tezosTicketerContent, + token: token as NonNativeEtherlinkToken + }) as Awaited>['operationResult']); + } - const withdrawalTransactionReceipt = await this.bridgeComponents.etherlink.withdraw({ - tezosReceiverAddress, - amount, - tezosTicketerContent, - etherlinkSenderAddress: etherlinkConnectedAddress, - etherlinkTokenProxyContractAddress, - tezosProxyAddress, - tezosTicketerAddress - }); - const bridgeTokenWithdrawal: StartWithdrawResult['tokenTransfer'] = { + const bridgeTokenWithdrawal: PendingBridgeTokenWithdrawal = { kind: BridgeTokenTransferKind.Withdrawal, status: BridgeTokenTransferStatus.Pending, - source: etherlinkUtils.toChecksumAddress(withdrawalTransactionReceipt.from), + source: etherlinkSourceAddress, receiver: tezosReceiverAddress, etherlinkOperation: { - hash: withdrawalTransactionReceipt.transactionHash.toString(), - timestamp: Date.now().toString(), + hash: operationResult.hash, amount, + timestamp: operationResult.timestamp, token, } }; @@ -318,36 +354,36 @@ export class TokenBridge implements Disposable { return { tokenTransfer: bridgeTokenWithdrawal, - startWithdrawOperation: withdrawalTransactionReceipt + operationResult }; } - async finishWithdraw(sealedBridgeTokenWithdrawal: SealedBridgeTokenWithdrawal): Promise { + async finishWithdraw( + sealedBridgeTokenWithdrawal: SealedBridgeTokenWithdrawal + ): Promise> { this.ensureIsNotDisposed(); - const finishWithdrawOperation = await this.bridgeComponents.tezos.finishWithdraw( - sealedBridgeTokenWithdrawal.rollupData.commitment, - sealedBridgeTokenWithdrawal.rollupData.proof - ); + + const operationResult = await this.bridgeComponents.tezosBridgeBlockchainService.finishWithdraw({ + cementedCommitment: sealedBridgeTokenWithdrawal.rollupData.commitment, + outputProof: sealedBridgeTokenWithdrawal.rollupData.proof + }); return { tokenTransfer: sealedBridgeTokenWithdrawal, - finishWithdrawOperation - }; + operationResult + } as FinishWithdrawResult; } - async getTezosConnectedAddress(): Promise { + async getTezosSignerAddress(): Promise { this.ensureIsNotDisposed(); - return this.tezosToolkit.wallet.pkh(); + + return this.bridgeComponents.tezosBridgeBlockchainService.getSignerAddress(); } - async getEtherlinkConnectedAddress(): Promise { + async getEtherlinkSignerAddress(): Promise { this.ensureIsNotDisposed(); - const accounts = await this.etherlinkToolkit.eth.getAccounts(); - const address = accounts[0] || this.etherlinkToolkit.eth.defaultAccount; - if (!address) - throw new Error('Address is unavailable'); - return address; + return this.bridgeComponents.etherlinkBridgeBlockchainService.getSignerAddress(); } [Symbol.dispose](): void { @@ -363,7 +399,7 @@ export class TokenBridge implements Disposable { if (guards.isDisposable(this.bridgeComponents.transfersBridgeDataProvider)) this.bridgeComponents.transfersBridgeDataProvider[Symbol.dispose](); - this.rejectAndClearAllStatusWatchers('The TokenBridge has been disposed!'); + this.rejectAndClearAllStatusWatchers(new TokenBridgeDisposed()); this.detachEvents(); clearInterval(this.lastCreatedTokenTransfersTimerId); this.lastCreatedTokenTransfers.clear(); @@ -379,18 +415,16 @@ export class TokenBridge implements Disposable { protected getBalances(accountAddress: string): Promise; protected getBalances(accountAddress: string, tokens: ReadonlyArray): Promise; - protected getBalances(accountAddress: string, offset: number, limit: number): Promise; + protected getBalances(accountAddress: string, fetchOptions: BalancesFetchOptions): Promise; protected getBalances( accountAddress: string, - tokensOrOffset?: ReadonlyArray | number, - limit?: number + tokensOfFetchOptions?: ReadonlyArray | BalancesFetchOptions ): Promise; protected getBalances( accountAddress: string, - tokensOrOffset?: ReadonlyArray | number, - limit?: number + tokensOfFetchOptions?: ReadonlyArray | BalancesFetchOptions ): Promise { - return this.bridgeComponents.balancesBridgeDataProvider.getBalances(accountAddress, tokensOrOffset, limit); + return this.bridgeComponents.balancesBridgeDataProvider.getBalances(accountAddress, tokensOfFetchOptions); } protected getRegisteredTokenPair(token: TezosToken | EtherlinkToken): Promise { @@ -398,53 +432,82 @@ export class TokenBridge implements Disposable { } protected getRegisteredTokenPairs(): Promise; - protected getRegisteredTokenPairs(offset: number, limit: number): Promise; - protected getRegisteredTokenPairs(offset?: number, limit?: number): Promise; - protected getRegisteredTokenPairs(offset?: number, limit?: number): Promise { - return this.bridgeComponents.tokensBridgeDataProvider.getRegisteredTokenPairs(offset, limit); + protected getRegisteredTokenPairs(fetchOptions: TokensFetchOptions): Promise; + protected getRegisteredTokenPairs(fetchOptions?: TokensFetchOptions): Promise; + protected getRegisteredTokenPairs(fetchOptions?: TokensFetchOptions): Promise { + return this.bridgeComponents.tokensBridgeDataProvider.getRegisteredTokenPairs(fetchOptions); } - protected getTokenTransfer(operationHash: string): Promise; - protected getTokenTransfer(tokenTransfer: BridgeTokenTransfer): Promise; - protected getTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise; - protected getTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise { - return this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfer(operationHashOrTokenTransfer); + protected getTokenTransfer(tokenTransferId: string): Promise { + return this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfer(tokenTransferId); } protected getTokenTransfers(): Promise; - protected getTokenTransfers(offset: number, limit: number): Promise; - protected getTokenTransfers(offset?: number, limit?: number): Promise; - protected getTokenTransfers(offset?: number, limit?: number): Promise { - return this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfers(offset, limit); + protected getTokenTransfers(fetchOptions: TransfersFetchOptions): Promise; + protected getTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise; + protected getTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise { + return this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfers(fetchOptions); } protected getAccountTokenTransfers(accountAddress: string): Promise; protected getAccountTokenTransfers(accountAddresses: readonly string[]): Promise; - protected getAccountTokenTransfers(accountAddress: string, offset: number, limit: number): Promise; - protected getAccountTokenTransfers(accountAddresses: readonly string[], offset: number, limit: number): Promise; - protected getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], offset?: number, limit?: number): Promise; - protected getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], offset?: number, limit?: number): Promise { + protected getAccountTokenTransfers(accountAddress: string, fetchOptions: TransfersFetchOptions): Promise; + protected getAccountTokenTransfers(accountAddresses: readonly string[], fetchOptions: TransfersFetchOptions): Promise; + protected getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], fetchOptions?: TransfersFetchOptions): Promise; + protected getAccountTokenTransfers(accountAddressOfAddresses: string | readonly string[], fetchOptions?: TransfersFetchOptions): Promise { return this.bridgeComponents.transfersBridgeDataProvider - .getAccountTokenTransfers(accountAddressOfAddresses, offset, limit); + .getAccountTokenTransfers(accountAddressOfAddresses, fetchOptions); + } + + protected getOperationTokenTransfers(operationHash: string): Promise; + protected getOperationTokenTransfers(tokenTransfer: BridgeTokenTransfer): Promise; + protected getOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise; + protected getOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): Promise { + return this.bridgeComponents.transfersBridgeDataProvider.getOperationTokenTransfers(operationHashOrTokenTransfer); + } + + protected async getSignerBalances(): Promise { + const [tezosSignerBalances, etherlinkSignerBalances] = await Promise.all([ + this.getTezosSignerAddress() + .then(signerAddress => signerAddress ? this.getBalances(signerAddress) : undefined), + this.getEtherlinkSignerAddress() + .then(signerAddress => signerAddress ? this.getBalances(signerAddress) : undefined), + ]); + + return { + tezosSignerBalances, + etherlinkSignerBalances + }; + } + + protected async getSignerTokenTransfers(): Promise; + protected async getSignerTokenTransfers(fetchOptions: TransfersFetchOptions): Promise; + protected async getSignerTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise; + protected async getSignerTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise { + const [tezosSignerAddress, etherlinkSignerAddress] = await Promise.all([ + this.getTezosSignerAddress(), + this.getEtherlinkSignerAddress() + ]); + + const addresses = tezosSignerAddress && etherlinkSignerAddress + ? [tezosSignerAddress, etherlinkSignerAddress] + : tezosSignerAddress || etherlinkSignerAddress; + + return addresses + ? this.getAccountTokenTransfers(addresses, fetchOptions) + : []; } // #endregion // #region Stream API - protected subscribeToTokenTransfer(transfer: BridgeTokenTransfer): void; - protected subscribeToTokenTransfer(operationHash: BridgeTokenTransfer): void; - protected subscribeToTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; - protected subscribeToTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { - this.bridgeComponents.transfersBridgeDataProvider.subscribeToTokenTransfer(operationHashOrTokenTransfer); + protected subscribeToTokenTransfer(tokenTransferId: string): void { + this.bridgeComponents.transfersBridgeDataProvider.subscribeToTokenTransfer(tokenTransferId); } - protected unsubscribeFromTokenTransfer(transfer: BridgeTokenTransfer): void; - protected unsubscribeFromTokenTransfer(operationHash: BridgeTokenTransfer): void; - protected unsubscribeFromTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; - protected unsubscribeFromTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; - protected unsubscribeFromTokenTransfer(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { - this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromTokenTransfer(operationHashOrTokenTransfer); + protected unsubscribeFromTokenTransfer(tokenTransferId: string): void { + this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromTokenTransfer(tokenTransferId); } protected subscribeToTokenTransfers(): void { @@ -469,23 +532,37 @@ export class TokenBridge implements Disposable { this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromAccountTokenTransfers(accountAddressOrAddresses); } + protected subscribeToOperationTokenTransfers(transfer: BridgeTokenTransfer): void; + protected subscribeToOperationTokenTransfers(operationHash: BridgeTokenTransfer): void; + protected subscribeToOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + protected subscribeToOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { + this.bridgeComponents.transfersBridgeDataProvider.subscribeToOperationTokenTransfers(operationHashOrTokenTransfer); + } + + protected unsubscribeFromOperationTokenTransfers(transfer: BridgeTokenTransfer): void; + protected unsubscribeFromOperationTokenTransfers(operationHash: BridgeTokenTransfer): void; + protected unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + protected unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void; + protected unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer: string | BridgeTokenTransfer): void { + this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer); + } + protected unsubscribeFromAllSubscriptions(): void { this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromAllSubscriptions(); - this.rejectAndClearAllStatusWatchers('Unsubscribe from all subscriptions'); } // #endregion - protected emitLocalTokenTransferCreatedEvent(tokenTransfer: TokenTransferCreatedEventArgument[0]) { + protected emitLocalTokenTransferCreatedEvent(tokenTransfer: BridgeTokenTransfer) { setTimeout(() => { this.emitTokenTransferCreatedOrUpdatedEvent(tokenTransfer); }, 0); } - protected emitTokenTransferCreatedOrUpdatedEvent(tokenTransfer: TokenTransferCreatedEventArgument[0]) { + protected emitTokenTransferCreatedOrUpdatedEvent(tokenTransfer: BridgeTokenTransfer) { this.ensureLastCreatedTokenTransfersTimerIsStarted(); - const initialOperationHash = bridgeUtils.getInitialOperationHash(tokenTransfer); + const initialOperationHash = bridgeUtils.getInitialOperation(tokenTransfer).hash; let eventName: 'tokenTransferCreated' | 'tokenTransferUpdated'; if (this.lastCreatedTokenTransfers.has(initialOperationHash)) { eventName = 'tokenTransferUpdated'; @@ -501,24 +578,38 @@ export class TokenBridge implements Disposable { } protected resolveStatusWatcherIfNeeded(tokenTransfer: BridgeTokenTransfer) { - const initialOperationHash = bridgeUtils.getInitialOperationHash(tokenTransfer); - const statusWatchers = this.tokenTransferOperationWatchers.get(initialOperationHash); - if (!statusWatchers) - return; + let statusWatcherKey: string; + let statusWatchers: ReturnType; + + if (tokenTransfer.status !== BridgeTokenTransferStatus.Pending) { + statusWatcherKey = tokenTransfer.id; + statusWatchers = this.tokenTransferStatusWatchers.get(tokenTransfer.id); + } + + if (!statusWatchers) { + statusWatcherKey = bridgeUtils.getInitialOperation(tokenTransfer).hash; + statusWatchers = this.tokenTransferStatusWatchers.get(statusWatcherKey); + + if (!statusWatchers) + return; + } for (const [status, watcher] of statusWatchers) { if (tokenTransfer.status >= status) { - watcher.resolve(tokenTransfer); + if (tokenTransfer.status === BridgeTokenTransferStatus.Failed) + watcher.reject(new FailedTokenTransferError(tokenTransfer)); + else + watcher.resolve(tokenTransfer); statusWatchers.delete(status); } } if (!statusWatchers.size) - this.tokenTransferOperationWatchers.delete(initialOperationHash); + this.tokenTransferStatusWatchers.delete(statusWatcherKey!); } protected rejectAndClearAllStatusWatchers(error: unknown) { - for (const statusWatchers of this.tokenTransferOperationWatchers.values()) { + for (const statusWatchers of this.tokenTransferStatusWatchers.values()) { for (const statusWatcher of statusWatchers.values()) { statusWatcher.reject(error); } @@ -526,7 +617,7 @@ export class TokenBridge implements Disposable { statusWatchers.clear(); } - this.tokenTransferOperationWatchers.clear(); + this.tokenTransferStatusWatchers.clear(); } protected attachEvents() { @@ -539,7 +630,7 @@ export class TokenBridge implements Disposable { this.bridgeComponents.transfersBridgeDataProvider.events.tokenTransferCreated.removeListener(this.handleTransfersBridgeDataProviderTokenTransferCreated); } - protected handleTransfersBridgeDataProviderTokenTransferCreated = (createdTokenTransfer: TokenTransferCreatedEventArgument[0]) => { + protected handleTransfersBridgeDataProviderTokenTransferCreated = (createdTokenTransfer: BridgeTokenTransfer) => { this.resolveStatusWatcherIfNeeded(createdTokenTransfer); this.emitTokenTransferCreatedOrUpdatedEvent(createdTokenTransfer); }; @@ -554,93 +645,7 @@ export class TokenBridge implements Disposable { return; loggerProvider.logger.error('Attempting to call the disposed TokenBridge instance'); - throw new DisposedError('TokenBridge is disposed'); - } - - private async depositUsingWalletApi( - token: TezosToken, - amount: bigint, - etherlinkReceiverAddress: string, - options: WalletDepositOptions | undefined | null, - ticketHelperOrNativeTokenTicketerContractAddress: string - ): Promise { - const isNativeToken = token.type === 'native'; - - loggerProvider.logger.log('Creating the deposit operation using Wallet API...'); - const [depositOperation, sourceAddress] = await Promise.all([ - isNativeToken - ? this.bridgeComponents.tezos.wallet.depositNativeToken({ - amount, - etherlinkReceiverAddress, - ticketHelperContractAddress: ticketHelperOrNativeTokenTicketerContractAddress, - }) - : this.bridgeComponents.tezos.wallet.depositNonNativeToken({ - token, - amount, - etherlinkReceiverAddress, - ticketHelperContractAddress: ticketHelperOrNativeTokenTicketerContractAddress - }), - this.getTezosConnectedAddress() - ]); - loggerProvider.logger.log('The deposit operation has been created:', depositOperation.opHash); - - return { - tokenTransfer: { - kind: BridgeTokenTransferKind.Deposit, - status: BridgeTokenTransferStatus.Pending, - source: sourceAddress, - receiver: etherlinkReceiverAddress, - tezosOperation: { - hash: depositOperation.opHash, - timestamp: new Date().toISOString(), - amount, - token, - } - }, - depositOperation - }; - } - - private async depositUsingContractApi( - token: TezosToken, - amount: bigint, - etherlinkReceiverAddress: string, - options: DepositOptions | undefined | null, - ticketHelperOrNativeTokenTicketerContractAddress: string - ): Promise { - const isNativeToken = token.type === 'native'; - - loggerProvider.logger.log('Creating the deposit operation using Contract API...'); - const depositOperation = await (isNativeToken - ? this.bridgeComponents.tezos.depositNativeToken({ - amount, - etherlinkReceiverAddress, - ticketHelperContractAddress: ticketHelperOrNativeTokenTicketerContractAddress - }) - : this.bridgeComponents.tezos.depositNonNativeToken({ - token, - amount, - etherlinkReceiverAddress, - ticketHelperContractAddress: ticketHelperOrNativeTokenTicketerContractAddress - }) - ); - loggerProvider.logger.log('The deposit operation has been created:', depositOperation.hash); - - return { - tokenTransfer: { - kind: BridgeTokenTransferKind.Deposit, - status: BridgeTokenTransferStatus.Pending, - source: depositOperation.source, - receiver: etherlinkReceiverAddress, - tezosOperation: { - hash: depositOperation.hash, - timestamp: new Date().toISOString(), - amount, - token, - } - }, - depositOperation - }; + throw new TokenBridgeDisposed(); } private ensureLastCreatedTokenTransfersTimerIsStarted() { @@ -660,11 +665,19 @@ export class TokenBridge implements Disposable { ); } - private async getTezosTicketerContent(tezosTicketerAddress: string): Promise { - const storage = await this.tezosToolkit.contract.getStorage<{ content: any }>(tezosTicketerAddress); - const content = [...Object.values(storage.content)]; - const contentMichelsonData = this.tezosTicketerContentSchema.Encode(content); + private async getRequiredTezosSignerAddress(): Promise { + const tezosSignerAddress = await this.getTezosSignerAddress(); + if (tezosSignerAddress) + return tezosSignerAddress; + + throw new TezosSignerAccountUnavailableError(); + } + + private async getRequiredEtherlinkSignerAddress(): Promise { + const tezosSignerAddress = await this.getEtherlinkSignerAddress(); + if (tezosSignerAddress) + return tezosSignerAddress; - return '0x' + packDataBytes(contentMichelsonData, tezosTicketerContentMichelsonType).bytes.slice(2); + throw new EtherlinkSignerAccountUnavailableError(); } } diff --git a/src/tokenBridge/tokenBridgeComponents.ts b/src/tokenBridge/tokenBridgeComponents.ts index 1c93bbd..d7db12c 100644 --- a/src/tokenBridge/tokenBridgeComponents.ts +++ b/src/tokenBridge/tokenBridgeComponents.ts @@ -1,10 +1,12 @@ +import { EtherlinkBridgeBlockchainService, TezosBridgeBlockchainService } from '../bridgeBlockchainService'; import type { BalancesBridgeDataProvider, TokensBridgeDataProvider, TransfersBridgeDataProvider } from '../bridgeDataProviders'; -import type { EtherlinkBlockchainBridgeComponent } from '../etherlink'; -import type { TezosBlockchainBridgeComponent } from '../tezos'; -export interface TokenBridgeComponents { - readonly tezos: TezosBlockchainBridgeComponent; - readonly etherlink: EtherlinkBlockchainBridgeComponent; +export interface TokenBridgeComponents< + TTezosBridgeBlockchainService extends TezosBridgeBlockchainService, + TEtherlinkBridgeBlockchainService extends EtherlinkBridgeBlockchainService +> { + readonly tezosBridgeBlockchainService: TTezosBridgeBlockchainService; + readonly etherlinkBridgeBlockchainService: TEtherlinkBridgeBlockchainService; readonly balancesBridgeDataProvider: BalancesBridgeDataProvider; readonly tokensBridgeDataProvider: TokensBridgeDataProvider; readonly transfersBridgeDataProvider: TransfersBridgeDataProvider; diff --git a/src/tokenBridge/tokenBridgeDataApi.ts b/src/tokenBridge/tokenBridgeDataApi.ts index 86283ca..5299348 100644 --- a/src/tokenBridge/tokenBridgeDataApi.ts +++ b/src/tokenBridge/tokenBridgeDataApi.ts @@ -1,6 +1,20 @@ -import type { TokensBridgeDataProvider, BalancesBridgeDataProvider, TransfersBridgeDataProvider } from '../bridgeDataProviders'; +import type { BridgeTokenTransfer } from '../bridgeCore'; +import type { + TokensBridgeDataProvider, BalancesBridgeDataProvider, TransfersBridgeDataProvider, + AccountTokenBalances, TransfersFetchOptions +} from '../bridgeDataProviders'; + +export interface SignerTokenBalances { + tezosSignerBalances?: AccountTokenBalances + etherlinkSignerBalances?: AccountTokenBalances; +} export interface TokenBridgeDataApi extends Pick, Pick, - Pick { + Pick { + getSignerBalances(): Promise; + + getSignerTokenTransfers(): Promise; + getSignerTokenTransfers(fetchOptions: TransfersFetchOptions): Promise; + getSignerTokenTransfers(fetchOptions?: TransfersFetchOptions): Promise; } diff --git a/src/tokenBridge/tokenBridgeOptions.ts b/src/tokenBridge/tokenBridgeOptions.ts index 86d8a84..4949863 100644 --- a/src/tokenBridge/tokenBridgeOptions.ts +++ b/src/tokenBridge/tokenBridgeOptions.ts @@ -1,35 +1,21 @@ -import type { TezosToolkit } from '@taquito/taquito'; -import type Web3 from 'web3'; - +import type { EtherlinkBridgeBlockchainService, TezosBridgeBlockchainService } from '../bridgeBlockchainService'; import type { TokensBridgeDataProvider, BalancesBridgeDataProvider, TransfersBridgeDataProvider } from '../bridgeDataProviders'; -interface TezosTokenBridgeOptions { - toolkit: TezosToolkit; - bridgeOptions: { - rollupAddress: string; - } -} - -interface EtherlinkTokenBridgeOptions { - toolkit: Web3; - bridgeOptions: { - kernelAddress: string; - withdrawPrecompileAddress: string; - } -} - interface DataProvidersTokenBridgeOptions { tokens: TokensBridgeDataProvider; balances: BalancesBridgeDataProvider; transfers: TransfersBridgeDataProvider } -export interface TokenBridgeOptions { - tezos: TezosTokenBridgeOptions; - etherlink: EtherlinkTokenBridgeOptions; +export interface TokenBridgeOptions< + TTezosBridgeBlockchainService extends TezosBridgeBlockchainService = TezosBridgeBlockchainService, + TEtherlinkBridgeBlockchainService extends EtherlinkBridgeBlockchainService = EtherlinkBridgeBlockchainService +> { + tezosBridgeBlockchainService: TTezosBridgeBlockchainService; + etherlinkBridgeBlockchainService: TEtherlinkBridgeBlockchainService; bridgeDataProviders: DataProvidersTokenBridgeOptions; } diff --git a/src/tokenBridge/tokenBridgeStreamApi.ts b/src/tokenBridge/tokenBridgeStreamApi.ts index d68ca3e..7ef7c44 100644 --- a/src/tokenBridge/tokenBridgeStreamApi.ts +++ b/src/tokenBridge/tokenBridgeStreamApi.ts @@ -5,9 +5,11 @@ export interface TokenBridgeStreamApi extends Pick< | 'subscribeToTokenTransfer' | 'subscribeToTokenTransfers' | 'subscribeToAccountTokenTransfers' + | 'subscribeToOperationTokenTransfers' | 'unsubscribeFromTokenTransfer' | 'unsubscribeFromTokenTransfers' | 'unsubscribeFromAccountTokenTransfers' + | 'unsubscribeFromOperationTokenTransfers' | 'unsubscribeFromAllSubscriptions' > { } diff --git a/src/etherlink/tokens.ts b/src/tokens/etherlinkTokens.ts similarity index 89% rename from src/etherlink/tokens.ts rename to src/tokens/etherlinkTokens.ts index 7afaec5..16d2c18 100644 --- a/src/etherlink/tokens.ts +++ b/src/tokens/etherlinkTokens.ts @@ -1,4 +1,4 @@ -import type { Token } from '../common'; +import type { Token } from './token'; export interface NativeEtherlinkToken extends Token { readonly type: 'native'; diff --git a/src/tokens/index.ts b/src/tokens/index.ts new file mode 100644 index 0000000..7cc20c9 --- /dev/null +++ b/src/tokens/index.ts @@ -0,0 +1,3 @@ +export type { Token } from './token'; +export type { TezosToken, NativeTezosToken, NonNativeTezosToken, FA12TezosToken, FA2TezosToken } from './tezosTokens'; +export type { EtherlinkToken, NativeEtherlinkToken, NonNativeEtherlinkToken, ERC20EtherlinkToken } from './etherlinkTokens'; diff --git a/src/tezos/tokens.ts b/src/tokens/tezosTokens.ts similarity index 91% rename from src/tezos/tokens.ts rename to src/tokens/tezosTokens.ts index f976f29..4541f6f 100644 --- a/src/tezos/tokens.ts +++ b/src/tokens/tezosTokens.ts @@ -1,4 +1,4 @@ -import type { Token } from '../common'; +import type { Token } from './token'; export interface NativeTezosToken extends Token { readonly type: 'native'; diff --git a/src/common/token.ts b/src/tokens/token.ts similarity index 100% rename from src/common/token.ts rename to src/tokens/token.ts diff --git a/src/utils/bridgeUtils.ts b/src/utils/bridgeUtils.ts index eb88706..51488af 100644 --- a/src/utils/bridgeUtils.ts +++ b/src/utils/bridgeUtils.ts @@ -1,15 +1,50 @@ import { BridgeTokenTransferKind, type BridgeTokenTransfer, type FinishedBridgeTokenDeposit } from '../bridgeCore'; +const tokenTransferIdSeparator = '_'; +const isEtherlinkTransaction = (operationHash: string) => operationHash.startsWith('0x'); + export const getInitialOperation = (tokenTransfer: BridgeTokenTransfer) => { return tokenTransfer.kind === BridgeTokenTransferKind.Deposit ? tokenTransfer.tezosOperation : tokenTransfer.etherlinkOperation; }; -export const getInitialOperationHash = (tokenTransfer: BridgeTokenTransfer): string => { - return tokenTransfer.kind === BridgeTokenTransferKind.Deposit - ? tokenTransfer.tezosOperation.hash - : tokenTransfer.etherlinkOperation.hash; +export function convertOperationDataToTokenTransferId(etherlinkOperationHash: string, logIndex: number): string; +export function convertOperationDataToTokenTransferId(tezosOperationHash: string, counter: number, nonce: number | null): string; +export function convertOperationDataToTokenTransferId(operationHash: string, logIndexOrCounter: number, nonce?: number | null): string; +export function convertOperationDataToTokenTransferId(operationHash: string, logIndexOrCounter: number, nonce?: number | null): string { + return !isEtherlinkTransaction(operationHash) && typeof nonce === 'number' + ? `${operationHash}${tokenTransferIdSeparator}${logIndexOrCounter.toString(10)}${tokenTransferIdSeparator}${nonce.toString(10)}` + : `${operationHash}${tokenTransferIdSeparator}${logIndexOrCounter.toString(10)}`; +} + +export const convertTokenTransferIdToOperationData = ( + tokenTransferId: string +): null + | readonly [tezosOperationHash: string, counter: number, nonce: number | null] + | readonly [etherlinkOperationHash: string, logIndex: number] => { + if (!tokenTransferId) + return null; + + try { + const operationData = tokenTransferId.split(tokenTransferIdSeparator); + if (!operationData[0] || !operationData[1]) + return null; + + const counterOrLogIndex = Number.parseInt(operationData[1]); + if (isEtherlinkTransaction(tokenTransferId)) + return [operationData[0], counterOrLogIndex]; + + + return operationData[2] + ? [operationData[0], counterOrLogIndex, Number.parseInt(operationData[2])] + : [operationData[0], counterOrLogIndex, null]; + } + catch { + // + } + + return null; }; export const isBridgeTokenTransferOwner = (tokenTransfer: BridgeTokenTransfer, address: string): boolean => { @@ -17,10 +52,11 @@ export const isBridgeTokenTransferOwner = (tokenTransfer: BridgeTokenTransfer, a }; const operationFieldNames: ReadonlyArray = ['tezosOperation', 'etherlinkOperation']; -const bigIntFieldNames: ReadonlyArray = ['amount', 'fee']; +const bigIntFieldNames: ReadonlyArray = ['amount']; const jsonStringifyReplacer = (_key: string, value: unknown) => typeof value === 'bigint' ? value.toString(10) : value; + export const stringifyBridgeTokenTransfer = (tokenTransfer: BridgeTokenTransfer, space?: string | number | undefined): string => { try { return JSON.stringify(tokenTransfer, jsonStringifyReplacer, space); @@ -30,7 +66,7 @@ export const stringifyBridgeTokenTransfer = (tokenTransfer: BridgeTokenTransfer, } }; -export const parseBridgeTokenTransfer = (tokenTransfer: string): string | null => { +export const parseBridgeTokenTransfer = (tokenTransfer: string): BridgeTokenTransfer | null => { try { const transfer = JSON.parse(tokenTransfer); diff --git a/src/utils/tezosUtils.ts b/src/utils/tezosUtils.ts index 08661a9..ad65592 100644 --- a/src/utils/tezosUtils.ts +++ b/src/utils/tezosUtils.ts @@ -1,7 +1,4 @@ -import type { BatchOperation, TransactionOperation } from '@taquito/taquito'; -import { encodePubKey, b58decode } from '@taquito/utils'; - -import { memoize } from './memoize'; +import { b58decode, encodeAddress } from '@taquito/utils'; export const convertAddressToBytes = (address: string, addPrefix = false): string => { const bytes = b58decode(address); @@ -13,9 +10,5 @@ export const convertBytesToAddress = (bytes: string): string => { if (bytes.startsWith('0x')) bytes = bytes.substring(2); - return encodePubKey(bytes); + return encodeAddress(bytes); }; - -export const getOperationTotalCost = memoize((operation: TransactionOperation | BatchOperation) => { - return operation.fee + Number.parseInt(operation.storageDiff) * 250; -}); diff --git a/src/utils/tokenUtils.ts b/src/utils/tokenUtils.ts index 3fc0473..49b5f16 100644 --- a/src/utils/tokenUtils.ts +++ b/src/utils/tokenUtils.ts @@ -1,7 +1,5 @@ import { isReadonlyArray } from './guards'; -import type { Token } from '../common'; -import type { EtherlinkToken } from '../etherlink'; -import type { TezosToken } from '../tezos'; +import type { Token, TezosToken, EtherlinkToken } from '../tokens'; export const isTezosToken = (token: Token): token is TezosToken => { return token.type === 'native' || token.type === 'fa1.2' || token.type === 'fa2'; diff --git a/tests/dipDupGraphQLQueryBuilder/dipDupGraphQLQueryBuilder.test.ts b/tests/dipDupGraphQLQueryBuilder/dipDupGraphQLQueryBuilder.test.ts index f20f249..61e0d66 100644 --- a/tests/dipDupGraphQLQueryBuilder/dipDupGraphQLQueryBuilder.test.ts +++ b/tests/dipDupGraphQLQueryBuilder/dipDupGraphQLQueryBuilder.test.ts @@ -1,8 +1,10 @@ import { - getTokenTransferQueryByTestCases, + getTokenTransferQueryTestCases, + getOperationTokenTransfersQueryTestCases, getTokenTransfersQueryTestCases, getTokenTransfersQueryByAccountAddressesTestCases, getTokenTransferSubscriptionTestCases, + getOperationTokenTransfersSubscriptionTestCases, getTokenTransfersSubscriptionsByAccountAddressesTestCases, getTokenTransfersSubscriptionsTestCases } from './testCases'; @@ -16,8 +18,18 @@ describe('DipDup GraphQL Query Builder', () => { queryBuilder = new DipDupGraphQLQueryBuilder(); }); - test.each(getTokenTransferQueryByTestCases)( + test.each(getTokenTransferQueryTestCases)( 'Build the getTokenTransfer query %s', + (_, testData) => { + const query = queryBuilder.getTokenTransferQuery(testData.operationHash, testData.counter || testData.logIndex, testData.nonce); + const preparedQuery = prepareQueryFormatting(query); + + expect(preparedQuery).toBe(testData.expectedQuery); + } + ); + + test.each(getOperationTokenTransfersQueryTestCases)( + 'Build the getOperationTokenTransfers query %s', (_, testData) => { const query = queryBuilder.getTokenTransferQuery(testData.operationHash); const preparedQuery = prepareQueryFormatting(query); @@ -47,7 +59,17 @@ describe('DipDup GraphQL Query Builder', () => { ); test.each(getTokenTransferSubscriptionTestCases)( - 'Build the getTokenTransferSubscription query %s', + 'Build the getOperationTokenTransferSubscription query %s', + (_, testData) => { + const subscription = queryBuilder.getTokenTransferSubscription(testData.operationHash, testData.counter || testData.logIndex, testData.nonce); + const preparedSubscription = prepareQueryFormatting(subscription); + + expect(preparedSubscription).toBe(testData.expectedQuery); + } + ); + + test.each(getOperationTokenTransfersSubscriptionTestCases)( + 'Build the getOperationTokenTransfersSubscription query %s', (_, testData) => { const subscription = queryBuilder.getTokenTransferSubscription(testData.operationHash); const preparedSubscription = prepareQueryFormatting(subscription); @@ -57,7 +79,7 @@ describe('DipDup GraphQL Query Builder', () => { ); test.each(getTokenTransfersSubscriptionsByAccountAddressesTestCases)( - 'Build the getTokenTransferSubscriptions query %s', + 'Build the getTokenTransfersSubscription query %s', (_, testData) => { const subscription = queryBuilder.getTokenTransfersStreamSubscription(testData.address, testData.startUpdatedAt); const preparedSubscription = prepareQueryFormatting(subscription); @@ -67,7 +89,7 @@ describe('DipDup GraphQL Query Builder', () => { ); test.each(getTokenTransfersSubscriptionsTestCases)( - 'Build the getTokenTransferSubscriptions query %s', + 'Build the getTokenTransfersSubscription query %s', (_, testData) => { const subscription = queryBuilder.getTokenTransfersStreamSubscription(null, testData.startUpdatedAt); const preparedSubscription = prepareQueryFormatting(subscription); diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/getOperationTokenTransfersQueryTestCases.ts b/tests/dipDupGraphQLQueryBuilder/testCases/getOperationTokenTransfersQueryTestCases.ts new file mode 100644 index 0000000..95507c9 --- /dev/null +++ b/tests/dipDupGraphQLQueryBuilder/testCases/getOperationTokenTransfersQueryTestCases.ts @@ -0,0 +1,24 @@ +/* eslint-disable max-len */ +import { PositiveTestCaseBase } from './testCase'; + +type TestCase = PositiveTestCaseBase<{ + operationHash: string +}>; +type TestCases = readonly TestCase[]; + +export const getOperationTokenTransfersQueryTestCases: TestCases = [ + [ + 'by Tezos operation hash', + { + operationHash: 'oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z', + expectedQuery: 'query TokenTransfer { bridge_operation(where: { _or : [ { deposit: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } } } } { withdrawal: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' + } + ], + [ + 'by Etherlink operation hash', + { + operationHash: '0x407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1', + expectedQuery: 'query TokenTransfer { bridge_operation(where: { _or : [ { withdrawal: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } } } } { deposit: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' + } + ] +]; diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/getOperationTokenTransfersSubscriptionTestCases.ts b/tests/dipDupGraphQLQueryBuilder/testCases/getOperationTokenTransfersSubscriptionTestCases.ts new file mode 100644 index 0000000..eead406 --- /dev/null +++ b/tests/dipDupGraphQLQueryBuilder/testCases/getOperationTokenTransfersSubscriptionTestCases.ts @@ -0,0 +1,24 @@ +/* eslint-disable max-len */ +import { PositiveTestCaseBase } from './testCase'; + +type TestCase = PositiveTestCaseBase<{ + operationHash: string, +}>; +type TestCases = readonly TestCase[]; + +export const getOperationTokenTransfersSubscriptionTestCases: TestCases = [ + [ + 'by Tezos operation hash', + { + operationHash: 'oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z', + expectedQuery: 'subscription TokenTransfer { bridge_operation(where: { _or : [ { deposit: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } } } } { withdrawal: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' + } + ], + [ + 'by Etherlink operation hash', + { + operationHash: '0x407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1', + expectedQuery: 'subscription TokenTransfer { bridge_operation(where: { _or : [ { withdrawal: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } } } } { deposit: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' + } + ] +]; diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransferQueryTestCases.ts b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransferQueryTestCases.ts index 9172257..2b0294b 100644 --- a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransferQueryTestCases.ts +++ b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransferQueryTestCases.ts @@ -2,23 +2,39 @@ import { PositiveTestCaseBase } from './testCase'; type TestCase = PositiveTestCaseBase<{ - operationHash: string -}>; + operationHash: string; + counter?: number; + nonce?: number; + logIndex?: number; +} & ( + | { counter: number; nonce?: number; logIndex?: undefined; } + | { counter?: undefined; nonce?: undefined; logIndex: number; })>; type TestCases = readonly TestCase[]; -export const getTokenTransferQueryByTestCases: TestCases = [ +export const getTokenTransferQueryTestCases: TestCases = [ [ - 'by Tezos operation hash', + 'by Tezos operation (without nonce)', { operationHash: 'oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z', - expectedQuery: 'query TokenTransfer { bridge_operation(where: { _or : [ { deposit: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } } } } { withdrawal: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + counter: 57383, + expectedQuery: 'query TokenTransfer { bridge_operation(where: { _or : [ { deposit: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } counter: { _eq: 57383 } } } } { withdrawal: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } counter: { _eq: 57383 } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ - 'by Etherlink operation hash', + 'by Tezos operation (with nonce)', + { + operationHash: 'oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z', + counter: 73027, + nonce: 15, + expectedQuery: 'query TokenTransfer { bridge_operation(where: { _or : [ { deposit: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } counter: { _eq: 73027 } nonce: { _eq: 15 } } } } { withdrawal: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } counter: { _eq: 73027 } nonce: { _eq: 15 } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' + } + ], + [ + 'by Etherlink operation', { operationHash: '0x407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1', - expectedQuery: 'query TokenTransfer { bridge_operation(where: { _or : [ { withdrawal: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } } } } { deposit: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + logIndex: 10, + expectedQuery: 'query TokenTransfer { bridge_operation(where: { _or : [ { withdrawal: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } log_index: { _eq: 10 } } } } { deposit: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } log_index: { _eq: 10 } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ] ]; diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransferSubscriptionTestCases.ts b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransferSubscriptionTestCases.ts index 2c53731..ffb9610 100644 --- a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransferSubscriptionTestCases.ts +++ b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransferSubscriptionTestCases.ts @@ -2,23 +2,39 @@ import { PositiveTestCaseBase } from './testCase'; type TestCase = PositiveTestCaseBase<{ - operationHash: string, -}>; + operationHash: string; + counter?: number; + nonce?: number; + logIndex?: number; +} & ( + | { counter: number; nonce?: number; logIndex?: undefined; } + | { counter?: undefined; nonce?: undefined; logIndex: number; })>; type TestCases = readonly TestCase[]; export const getTokenTransferSubscriptionTestCases: TestCases = [ [ - 'by Tezos operation hash', + 'by Tezos operation (without nonce)', { operationHash: 'oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z', - expectedQuery: 'subscription TokenTransfer { bridge_operation(where: { _or : [ { deposit: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } } } } { withdrawal: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + counter: 57383, + expectedQuery: 'subscription TokenTransfer { bridge_operation(where: { _or : [ { deposit: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } counter: { _eq: 57383 } } } } { withdrawal: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } counter: { _eq: 57383 } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ - 'by Etherlink operation hash', + 'by Tezos operation (with nonce)', + { + operationHash: 'oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z', + counter: 73027, + nonce: 15, + expectedQuery: 'subscription TokenTransfer { bridge_operation(where: { _or : [ { deposit: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } counter: { _eq: 73027 } nonce: { _eq: 15 } } } } { withdrawal: { l1_transaction: { operation_hash: { _eq: "oocHgjpnSz9A8LRPekG4Fxspc2FW7wiBj7i3nMLpTaj4KQrSH2z" } counter: { _eq: 73027 } nonce: { _eq: 15 } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' + } + ], + [ + 'by Etherlink operation', { operationHash: '0x407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1', - expectedQuery: 'subscription TokenTransfer { bridge_operation(where: { _or : [ { withdrawal: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } } } } { deposit: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + logIndex: 10, + expectedQuery: 'subscription TokenTransfer { bridge_operation(where: { _or : [ { withdrawal: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } log_index: { _eq: 10 } } } } { deposit: { l2_transaction: { transaction_hash: { _eq: "407671cc863e14b40c404ee9bd8f7281516f44d9b4785f006dc43b4f4ef8d2f1" } log_index: { _eq: 10 } } } } ] }) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ] ]; diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersQueryByAccountAddressesTestCases.ts b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersQueryByAccountAddressesTestCases.ts index fdffb1b..267e826 100644 --- a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersQueryByAccountAddressesTestCases.ts +++ b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersQueryByAccountAddressesTestCases.ts @@ -15,7 +15,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: 'tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF', offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } { withdrawal: { l2_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } { withdrawal: { l2_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -24,7 +24,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: ['tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF'], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } { withdrawal: { l2_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } { withdrawal: { l2_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -33,7 +33,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: '0x4A1819c83A78C948db50f80fED82721Dd0401c9b', offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } { withdrawal: { l2_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } { withdrawal: { l2_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -42,7 +42,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: ['0x4A1819c83A78C948db50f80fED82721Dd0401c9b'], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } { withdrawal: { l2_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } { withdrawal: { l2_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], @@ -52,7 +52,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: ['tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF', 'tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo'], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } } } } { withdrawal: { l2_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } } } } { withdrawal: { l2_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -61,7 +61,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: ['0x4A1819c83A78C948db50f80fED82721Dd0401c9b', '0xBefD2C6fFC36249ebEbd21d6DF6376ecF3BAc448'], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } } } { withdrawal: { l2_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } } } { withdrawal: { l2_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -70,7 +70,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: ['tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF', 'tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo', 'tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns'], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } } } } { withdrawal: { l2_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } } } } { withdrawal: { l2_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -79,7 +79,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: ['0x4A1819c83A78C948db50f80fED82721Dd0401c9b', '0xBefD2C6fFC36249ebEbd21d6DF6376ecF3BAc448', '0xce912ad4F73dBC149110091044a8f58Fd17B2b53'], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } } } { withdrawal: { l2_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } } } { withdrawal: { l2_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], @@ -89,7 +89,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ address: ['tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF', '0x4A1819c83A78C948db50f80fED82721Dd0401c9b'], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } }{ l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } }{ l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } ] } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } }{ l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } }{ l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } ] } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -103,7 +103,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ ], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } ] } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } ] } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -119,7 +119,7 @@ export const getTokenTransfersQueryByAccountAddressesTestCases: TestCases = [ ], offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } ] } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } ] } } } ] }, order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ] ]; diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersQueryTestCases.ts b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersQueryTestCases.ts index 6f13181..2e0bb0a 100644 --- a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersQueryTestCases.ts +++ b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersQueryTestCases.ts @@ -13,7 +13,7 @@ export const getTokenTransfersQueryTestCases: TestCases = [ { offset: 0, limit: 100, - expectedQuery: 'query TokenTransfers { bridge_operation( order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( order_by: { created_at: desc }, offset: 0, limit: 100 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -21,7 +21,7 @@ export const getTokenTransfersQueryTestCases: TestCases = [ { offset: 300, limit: 10, - expectedQuery: 'query TokenTransfers { bridge_operation( order_by: { created_at: desc }, offset: 300, limit: 10 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'query TokenTransfers { bridge_operation( order_by: { created_at: desc }, offset: 300, limit: 10 ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], ]; diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersSubscriptionsByAccountAddressesTestCases.ts b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersSubscriptionsByAccountAddressesTestCases.ts index e9175f9..0d63f05 100644 --- a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersSubscriptionsByAccountAddressesTestCases.ts +++ b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersSubscriptionsByAccountAddressesTestCases.ts @@ -13,7 +13,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: 'tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF', startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } { withdrawal: { l2_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } { withdrawal: { l2_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -21,7 +21,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: ['tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF'], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } { withdrawal: { l2_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } { withdrawal: { l2_transaction: { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -29,7 +29,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: '0x4A1819c83A78C948db50f80fED82721Dd0401c9b', startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } { withdrawal: { l2_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } { withdrawal: { l2_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -37,7 +37,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: ['0x4A1819c83A78C948db50f80fED82721Dd0401c9b'], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } { withdrawal: { l2_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } { withdrawal: { l2_transaction: { l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], @@ -46,7 +46,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: ['tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF', 'tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo'], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } } } } { withdrawal: { l2_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } } } } { withdrawal: { l2_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -54,7 +54,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: ['0x4A1819c83A78C948db50f80fED82721Dd0401c9b', '0xBefD2C6fFC36249ebEbd21d6DF6376ecF3BAc448'], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } } } { withdrawal: { l2_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } } } { withdrawal: { l2_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -62,7 +62,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: ['tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF', 'tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo', 'tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns'], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } } } } { withdrawal: { l2_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } } } } { withdrawal: { l2_transaction: { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -70,7 +70,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: ['0x4A1819c83A78C948db50f80fED82721Dd0401c9b', '0xBefD2C6fFC36249ebEbd21d6DF6376ecF3BAc448', '0xce912ad4F73dBC149110091044a8f58Fd17B2b53'], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } } } { withdrawal: { l2_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } } } { withdrawal: { l2_transaction: { l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], @@ -79,7 +79,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase { address: ['tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF', '0x4A1819c83A78C948db50f80fED82721Dd0401c9b'], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } }{ l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } }{ l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } ] } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } }{ l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _eq: "tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF" } }{ l2_account: { _eq: "4a1819c83a78c948db50f80fed82721dd0401c9b" } } ] } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -92,7 +92,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase 'tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo' ], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } ] } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448"] } } ] } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], [ @@ -107,7 +107,7 @@ export const getTokenTransfersSubscriptionsByAccountAddressesTestCases: TestCase '0xce912ad4F73dBC149110091044a8f58Fd17B2b53' ], startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } ] } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( where: { _or: [ { deposit: { l1_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } ] } } } { withdrawal: { l2_transaction: { _or: [ { l1_account: { _in: ["tz1M6VFkpALGXYoP5CvobR3z1pYu7KvirpMF","tz1YG6P2GTQKFpd9jeuESam2vg6aA9HHRkKo","tz1N96ka7u5cKVbrLdK6wa6KyLoAgdfDzKns"] } }{ l2_account: { _in: ["4a1819c83a78c948db50f80fed82721dd0401c9b","befd2c6ffc36249ebebd21d6df6376ecf3bac448","ce912ad4f73dbc149110091044a8f58fd17b2b53"] } } ] } } } ] }, batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ] ]; diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersSubscriptionsTestCases.ts b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersSubscriptionsTestCases.ts index c205eb1..973c2c2 100644 --- a/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersSubscriptionsTestCases.ts +++ b/tests/dipDupGraphQLQueryBuilder/testCases/getTokenTransfersSubscriptionsTestCases.ts @@ -11,7 +11,7 @@ export const getTokenTransfersSubscriptionsTestCases: TestCases = [ 'all', { startUpdatedAt: new Date('2024-03-17T12:13:10.104Z'), - expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash timestamp } l2_transaction { level transaction_hash amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof } } } } }' + expectedQuery: 'subscription TokenTransfers { bridge_operation_stream( batch_size: 10, cursor: {initial_value: {updated_at: "2024-03-17T12:13:10.104Z"}, ordering: ASC} ) { type is_completed is_successful created_at updated_at deposit { l1_transaction { level operation_hash counter nonce amount ticket { token { type contract_address token_id } } l1_account l2_account timestamp inbox_message { type level index } } l2_transaction { level transaction_hash log_index amount l2_token { id } timestamp } } withdrawal { l1_transaction { level operation_hash counter nonce timestamp } l2_transaction { level transaction_hash log_index amount l2_token { id ticket { token { type contract_address token_id } } } l1_account l2_account timestamp outbox_message { level index commitment { hash } proof cemented_at } } } } }' } ], ]; diff --git a/tests/dipDupGraphQLQueryBuilder/testCases/index.ts b/tests/dipDupGraphQLQueryBuilder/testCases/index.ts index 34ac871..245ccb7 100644 --- a/tests/dipDupGraphQLQueryBuilder/testCases/index.ts +++ b/tests/dipDupGraphQLQueryBuilder/testCases/index.ts @@ -1,6 +1,9 @@ -export { getTokenTransferQueryByTestCases } from './getTokenTransferQueryTestCases'; +export { getTokenTransferQueryTestCases } from './getTokenTransferQueryTestCases'; +export { getOperationTokenTransfersQueryTestCases } from './getOperationTokenTransfersQueryTestCases'; export { getTokenTransfersQueryByAccountAddressesTestCases } from './getTokenTransfersQueryByAccountAddressesTestCases'; export { getTokenTransfersQueryTestCases } from './getTokenTransfersQueryTestCases'; + export { getTokenTransferSubscriptionTestCases } from './getTokenTransferSubscriptionTestCases'; +export { getOperationTokenTransfersSubscriptionTestCases } from './getOperationTokenTransfersSubscriptionTestCases'; export { getTokenTransfersSubscriptionsByAccountAddressesTestCases } from './getTokenTransfersSubscriptionsByAccountAddressesTestCases'; export { getTokenTransfersSubscriptionsTestCases } from './getTokenTransfersSubscriptionsTestCases';