diff --git a/packages/sequencer/src/settlement/SettlementModule.ts b/packages/sequencer/src/settlement/SettlementModule.ts index a7edcdd9..597da53b 100644 --- a/packages/sequencer/src/settlement/SettlementModule.ts +++ b/packages/sequencer/src/settlement/SettlementModule.ts @@ -9,7 +9,15 @@ import { SettlementSmartContractBase, DynamicBlockProof, } from "@proto-kit/protocol"; -import { AccountUpdate, Mina, PublicKey, TokenContract, TokenId } from "o1js"; +import { + AccountUpdate, + fetchAccount, + Field, + Mina, + PublicKey, + TokenContract, + TokenId, +} from "o1js"; import { inject } from "tsyringe"; import { EventEmitter, @@ -351,4 +359,49 @@ export class SettlementModule ).bridgeContractMina(), }; } + + public async checkDeployment( + tokenBridges?: Array<{ address: PublicKey; tokenId: Field }> + ): Promise { + const contracts: Array<{ address: PublicKey; tokenId?: Field }> = [ + ...this.getContractAddresses().map((addr) => ({ address: addr })), + ...(tokenBridges ?? []), + ]; + + const isLocal = this.baseLayer.isLocalBlockChain(); + const missing: Array<{ address: string; error: string }> = []; + + await Promise.all( + contracts.map(async ({ address, tokenId }) => { + if (isLocal) { + if (!Mina.hasAccount(address, tokenId)) { + missing.push({ + address: address.toBase58(), + error: "Not found on local chain", + }); + } + } else { + const { account, error } = await fetchAccount({ + publicKey: address, + tokenId, + }); + if (account === null || account === undefined) { + missing.push({ + address: address.toBase58(), + error: error?.statusText ?? "Not found on chain", + }); + } + } + }) + ); + + if (missing.length > 0) { + const errorList = missing + .map((m) => ` ${m.address}: ${m.error}`) + .join("\n"); + throw new Error(` + Missing contracts:\n${errorList} + `); + } + } } diff --git a/packages/sequencer/test/settlement/Settlement.ts b/packages/sequencer/test/settlement/Settlement.ts index 179d2d7f..cfc97c04 100644 --- a/packages/sequencer/test/settlement/Settlement.ts +++ b/packages/sequencer/test/settlement/Settlement.ts @@ -312,6 +312,20 @@ export const settlementTestFn = ( let user0Nonce = 0; let acc0L2Nonce = 0; + it("should throw error", async () => { + const deploymentPromise = + tokenConfig === undefined + ? settlementModule.checkDeployment() + : settlementModule.checkDeployment([ + { + address: tokenBridgeKey.toPublicKey(), + tokenId: tokenOwner!.deriveTokenId(), + }, + ]); + + await expect(deploymentPromise).rejects.toThrow(); + }); + it( "should deploy", async () => { @@ -578,6 +592,12 @@ export const settlementTestFn = ( .proveAndSendTransaction(tx, "included"); const actions = await Mina.fetchActions(dispatch.address); + if (baseLayerConfig.network.type !== "local") { + await fetchAccount({ + publicKey: tokenBridgeKey.toPublicKey(), + tokenId: bridgedTokenId, + }); + } const balanceDiff = bridge.account.balance .get() .sub(contractBalanceBefore); @@ -684,12 +704,11 @@ export const settlementTestFn = ( expect(settlementResult.bridgeTransactions).toHaveLength(2); - if (baseLayerConfig.network.type !== "local") { - await fetchAccount({ - publicKey: userKey.toPublicKey(), - tokenId: bridgingContract.deriveTokenId(), - }); - } + await settlementModule.utils.fetchContractAccounts({ + address: userKey.toPublicKey(), + tokenId: bridgingContract.deriveTokenId(), + }); + const account = Mina.getAccount( userKey.toPublicKey(), bridgingContract.deriveTokenId() @@ -780,4 +799,21 @@ export const settlementTestFn = ( }, timeout ); + + it("should not throw error after settlement", async () => { + expect.assertions(1); + + // Obtain promise of deployment check + const deploymentCheckPromise = + tokenConfig === undefined + ? settlementModule.checkDeployment() + : settlementModule.checkDeployment([ + { + address: tokenBridgeKey.toPublicKey(), + tokenId: tokenOwner!.deriveTokenId(), + }, + ]); + + await expect(deploymentCheckPromise).resolves.toBeUndefined(); + }); };