UNPKG

stellar-plus

Version:

beta version of stellar-plus, an all-in-one sdk for the Stellar blockchain

541 lines (540 loc) 27.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); /* eslint-disable @typescript-eslint/no-unsafe-assignment */ const stellar_sdk_1 = require("@stellar/stellar-sdk"); const constants_1 = require("../../../stellar-plus/asset/soroban-token/constants"); const soroban_transaction_1 = require("../../../stellar-plus/core/pipelines/soroban-transaction"); const error_1 = require("../../../stellar-plus/error"); const network_1 = require("../../../stellar-plus/network"); const rpc_1 = require("../../../stellar-plus/rpc"); const utils_1 = require("../../../tests/utils"); const errors_1 = require("./errors"); const contract_engine_1 = require("../contract-engine"); jest.mock('stellar-plus/core/pipelines/soroban-transaction', () => ({ SorobanTransactionPipeline: jest.fn(), })); jest.mock('stellar-plus/rpc/default-handler', () => ({ DefaultRpcHandler: jest.fn(), })); const MOCKED_SOROBAN_TRANSACTION_PIPELINE = soroban_transaction_1.SorobanTransactionPipeline; const MOCKED_DEFAULT_RPC_HANDLER = rpc_1.DefaultRpcHandler; const MOCKED_CONTRACT_ID = 'CBJT4BOMRHYKHZ6HF3QG4YR7Q63BE44G73M4MALDTQ3SQVUZDE7GN35I'; const MOCKED_WASM_HASH = 'eb94566536d7f56c353b4760f6e359eca3631b70d295820fb6de55a796e019ae'; const MOCKED_CONTRACT_SPEC = constants_1.spec; const MOCKED_WASM_FILE = Buffer.from('mockWasm', 'utf-8'); const MOCKED_STELLAR_ASSET = stellar_sdk_1.Asset.native(); const MOCKED_CONTRACT_CODE_KEY = new stellar_sdk_1.xdr.LedgerKeyContractCode({ hash: Buffer.from(MOCKED_WASM_HASH, 'hex'), }); const NETWORK_CONFIG = (0, network_1.TestNet)(); const MOCKED_TX_INVOCATION = { header: { source: 'GACF23GKVFTU77K6W6PWSVN7YBM63UHDULILIEXJO6FR4YKMJ7FW3DTI', fee: '100', timeout: 100, }, signers: [], }; const MOCKED_SOROBAN_INVOKE_ARGS = Object.assign({ method: constants_1.methods.name, methodArgs: {} }, MOCKED_TX_INVOCATION); describe('ContractEngine', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Intialization', () => { it('should initialize with wasm file and provided spec', () => { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasm: MOCKED_WASM_FILE, spec: MOCKED_CONTRACT_SPEC, }, }); expect(contractEngine.getWasm()).toEqual(MOCKED_WASM_FILE); }); it('should initialize with wasm hash', () => { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); expect(contractEngine.getWasmHash()).toEqual(MOCKED_WASM_HASH); }); it('should initialize with contract id', () => { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, spec: MOCKED_CONTRACT_SPEC, }, }); expect(contractEngine.getContractId()).toEqual(MOCKED_CONTRACT_ID); }); it('should load the spec from the wasm file', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const helloWorldWasm = yield (0, utils_1.loadWasmFile)('./src/tests/contracts/hello-world/hello_world.wasm'); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasm: helloWorldWasm, }, }); yield contractEngine.loadSpecFromWasm(); // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(contractEngine.spec).toBeDefined(); })); }); describe('Initialization Errors', () => { it('should throw error if wasm file is required but is not present', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { spec: MOCKED_CONTRACT_SPEC, wasmHash: MOCKED_WASM_HASH, }, }); expect(() => contractEngine.getWasm()).toThrow(errors_1.CEError.missingWasm()); yield expect(contractEngine.uploadWasm(MOCKED_TX_INVOCATION)).rejects.toThrow(errors_1.CEError.missingWasm()); })); it('should throw error if wasm hash is required but is not present', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { spec: MOCKED_CONTRACT_SPEC, wasm: MOCKED_WASM_FILE, }, }); expect(() => contractEngine.getWasmHash()).toThrow(errors_1.CEError.missingWasmHash()); yield expect(contractEngine.deploy(MOCKED_TX_INVOCATION)).rejects.toThrow(errors_1.CEError.missingWasmHash()); yield expect(contractEngine.getContractCodeLiveUntilLedgerSeq()).rejects.toThrow(errors_1.CEError.missingWasmHash()); yield expect(contractEngine.getContractInstanceLiveUntilLedgerSeq()).rejects.toThrow(errors_1.CEError.missingWasmHash()); yield expect(contractEngine.restoreContractCode(MOCKED_TX_INVOCATION)).rejects.toThrow(errors_1.CEError.missingWasmHash()); yield expect(contractEngine.restoreContractInstance(MOCKED_TX_INVOCATION)).rejects.toThrow(errors_1.CEError.missingWasmHash()); })); it('should throw error if contract id is required but is not present', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { spec: MOCKED_CONTRACT_SPEC, wasmHash: MOCKED_WASM_HASH, }, }); expect(() => contractEngine.getContractId()).toThrow(errors_1.CEError.missingContractId()); expect(() => contractEngine.getContractFootprint()).toThrow(errors_1.CEError.missingContractId()); yield expect(contractEngine.invokeContract(MOCKED_SOROBAN_INVOKE_ARGS)).rejects.toThrow(errors_1.CEError.missingContractId()); yield expect(contractEngine.readFromContract(MOCKED_SOROBAN_INVOKE_ARGS)).rejects.toThrow(errors_1.CEError.missingContractId()); yield expect(contractEngine.runTransactionPipeline(MOCKED_SOROBAN_INVOKE_ARGS)).rejects.toThrow(errors_1.CEError.missingContractId()); })); it('should throw if spec is required but not present', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, }, }); yield expect(() => contractEngine.readFromContract(Object.assign({ method: constants_1.methods.name, methodArgs: {} }, MOCKED_TX_INVOCATION))).rejects.toThrow(errors_1.CEError.missingSpec()); })); }); describe('Initialization workflow', () => { it('should upload wasm', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockResolvedValue({ sorobanGetTransactionPipelineOutput: { output: { wasmHash: MOCKED_WASM_HASH, }, }, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasm: MOCKED_WASM_FILE, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.uploadWasm(MOCKED_TX_INVOCATION)).resolves.toBeDefined(); expect(contractEngine.getWasm()).toEqual(MOCKED_WASM_FILE); })); it('should deploy contract', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockResolvedValue({ sorobanGetTransactionPipelineOutput: { output: { contractId: MOCKED_CONTRACT_ID, }, }, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.deploy(MOCKED_TX_INVOCATION)).resolves.toBeDefined(); expect(contractEngine.getContractId()).toEqual(MOCKED_CONTRACT_ID); })); }); describe('Additional getters', () => { it('should return contract footprint', () => { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, spec: MOCKED_CONTRACT_SPEC, }, }); const footprint = new stellar_sdk_1.Contract(MOCKED_CONTRACT_ID).getFootprint(); expect(contractEngine.getContractFootprint()).toEqual(footprint); }); it('should return the rpc handler', () => { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, spec: MOCKED_CONTRACT_SPEC, }, }); expect(contractEngine.getRpcHandler()).toBeDefined(); }); it('should throw if contract code is missing the live until ledger seq', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_DEFAULT_RPC_HANDLER.mockImplementation(() => { return { getLedgerEntries: jest.fn().mockResolvedValue({ entries: [ { key: stellar_sdk_1.xdr.LedgerKey.contractCode(MOCKED_CONTRACT_CODE_KEY), xdr: 'xdr', }, ], latestLedger: 1, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.getContractCodeLiveUntilLedgerSeq()).rejects.toThrow(errors_1.CEError.contractCodeMissingLiveUntilLedgerSeq()); })); it('should return the live until ledger seq for contract code', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_DEFAULT_RPC_HANDLER.mockImplementation(() => { return { getLedgerEntries: jest.fn().mockResolvedValue({ entries: [ { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment key: Object.assign(stellar_sdk_1.xdr.LedgerKey.contractCode(MOCKED_CONTRACT_CODE_KEY)), xdr: 'xdr', liveUntilLedgerSeq: 1, }, ], latestLedger: 1, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.getContractCodeLiveUntilLedgerSeq()).resolves.toEqual(1); })); it('should throw if contract instance is missing the live until ledger seq', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const footprint = new stellar_sdk_1.Contract(MOCKED_CONTRACT_ID).getFootprint(); MOCKED_DEFAULT_RPC_HANDLER.mockImplementation(() => { return { getLedgerEntries: jest.fn().mockResolvedValue({ entries: [ { key: footprint, xdr: 'xdr', }, ], latestLedger: 1, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.getContractInstanceLiveUntilLedgerSeq()).rejects.toThrow(errors_1.CEError.contractInstanceMissingLiveUntilLedgerSeq()); })); it('should return the live until ledger seq for contract instance', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const footprint = new stellar_sdk_1.Contract(MOCKED_CONTRACT_ID).getFootprint(); MOCKED_DEFAULT_RPC_HANDLER.mockImplementation(() => { return { getLedgerEntries: jest.fn().mockResolvedValue({ entries: [ { key: footprint, xdr: 'xdr', liveUntilLedgerSeq: 1, }, ], latestLedger: 1, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.getContractInstanceLiveUntilLedgerSeq()).resolves.toEqual(1); })); }); describe('Contract restore workflows', () => { it('should fail to restore a contract code when contract code is not found', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_DEFAULT_RPC_HANDLER.mockImplementation(() => { return { getLedgerEntries: jest.fn().mockResolvedValue({ entries: [], latestLedger: 1, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.restoreContractCode(MOCKED_TX_INVOCATION)).rejects.toThrow(errors_1.CEError.contractCodeNotFound({ entries: [], latestLedger: 1, })); })); it('should restore a contract code', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_DEFAULT_RPC_HANDLER.mockImplementation(() => { return { getLedgerEntries: jest.fn().mockResolvedValue({ entries: [ { key: stellar_sdk_1.xdr.LedgerKey.contractCode(MOCKED_CONTRACT_CODE_KEY), xdr: 'xdr', }, ], latestLedger: 1, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.restoreContractCode(MOCKED_TX_INVOCATION)).resolves.toBeUndefined(); })); it('should fail to restore a contract instance when contract instance is not found', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_DEFAULT_RPC_HANDLER.mockImplementation(() => { return { getLedgerEntries: jest.fn().mockResolvedValue({ entries: [], latestLedger: 1, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.restoreContractInstance(MOCKED_TX_INVOCATION)).rejects.toThrow(errors_1.CEError.contractInstanceNotFound({ entries: [], latestLedger: 1, })); })); it('should restore a contract instance', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const footprint = new stellar_sdk_1.Contract(MOCKED_CONTRACT_ID).getFootprint(); MOCKED_DEFAULT_RPC_HANDLER.mockImplementation(() => { return { getLedgerEntries: jest.fn().mockResolvedValue({ entries: [ { key: footprint, xdr: 'xdr', }, ], latestLedger: 1, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.restoreContractInstance(MOCKED_TX_INVOCATION)).resolves.toBeUndefined(); })); }); describe('Contract invocation', () => { it('should not wrap and deploy with a contract id', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.wrapAndDeployClassicAsset(Object.assign({ asset: MOCKED_STELLAR_ASSET }, MOCKED_TX_INVOCATION))).rejects.toThrow(errors_1.CEError.contractIdAlreadySet()); })); it('should wrap and deploy a classic asset', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockResolvedValue({ sorobanGetTransactionPipelineOutput: { output: { contractId: MOCKED_CONTRACT_ID, }, }, }), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.wrapAndDeployClassicAsset(Object.assign({ asset: MOCKED_STELLAR_ASSET }, MOCKED_TX_INVOCATION))).resolves.toBeDefined(); expect(contractEngine.getContractId()).toEqual(MOCKED_CONTRACT_ID); })); it('should surface exceptions from the transaction pipeline when wrapping and deploying a classic asset', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockRejectedValue(error_1.StellarPlusError.unexpectedError()), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.wrapAndDeployClassicAsset(Object.assign({ asset: MOCKED_STELLAR_ASSET }, MOCKED_TX_INVOCATION))).rejects.toThrow(errors_1.CEError.failedToWrapAsset(error_1.StellarPlusError.unexpectedError())); })); it('should surface exceptions from the transaction pipeline when deploying a contract', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockRejectedValue(error_1.StellarPlusError.unexpectedError()), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasm: MOCKED_WASM_FILE, wasmHash: MOCKED_WASM_HASH, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.deploy(MOCKED_TX_INVOCATION)).rejects.toThrow(errors_1.CEError.failedToDeployContract(error_1.StellarPlusError.unexpectedError())); })); it('should surface exceptions from the transaction pipeline when uploading a wasm', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockRejectedValue(error_1.StellarPlusError.unexpectedError()), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { wasm: MOCKED_WASM_FILE, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.uploadWasm(MOCKED_TX_INVOCATION)).rejects.toThrow(errors_1.CEError.failedToUploadWasm(error_1.StellarPlusError.unexpectedError())); })); it('should surface exceptions from the transaction pipeline when invoking a contract', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockRejectedValue(error_1.StellarPlusError.unexpectedError()), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.invokeContract(MOCKED_SOROBAN_INVOKE_ARGS)).rejects.toThrow(error_1.StellarPlusError.unexpectedError()); })); it('should surface exceptions from the transaction pipeline when reading from a contract', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockRejectedValue(error_1.StellarPlusError.unexpectedError()), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.readFromContract(MOCKED_SOROBAN_INVOKE_ARGS)).rejects.toThrow(error_1.StellarPlusError.unexpectedError()); })); it('should invoke a contract', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockResolvedValue(true), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.invokeContract(MOCKED_SOROBAN_INVOKE_ARGS)).resolves.toEqual(true); })); it('should read from a contract', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { MOCKED_SOROBAN_TRANSACTION_PIPELINE.mockImplementation(() => { return { execute: jest.fn().mockResolvedValue(true), }; }); const contractEngine = new contract_engine_1.ContractEngine({ networkConfig: NETWORK_CONFIG, contractParameters: { contractId: MOCKED_CONTRACT_ID, spec: MOCKED_CONTRACT_SPEC, }, }); yield expect(contractEngine.readFromContract(MOCKED_SOROBAN_INVOKE_ARGS)).resolves.toEqual(true); })); }); });