stellar-plus
Version:
beta version of stellar-plus, an all-in-one sdk for the Stellar blockchain
541 lines (540 loc) • 27.5 kB
JavaScript
"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);
}));
});
});