MAST Contract Paths
MAST Contract Paths is a powerful concept utilizing OP_EXEC which allows to pack a lot of user logic into seemingly small contract. In fact the MCP's template body is an engine to verify Merkle proof that a subscript belongs to a greater subset of contract routines belonging together. MCP concept also allows the subscript routines to be not revealed to the outer world until they are executed first time, allowing for greater security. The subscripts represent Merklized Abstract Syntax Tree (MAST) concept. At current consensus rules an MCP contract could contain up to 524288 (2^19) subscripts which are also subject to consensus rules (see nexa spec).
Contract syntax
MCP contracts have a distinct syntax and a set of rules to follow, see example:
pragma nexscript ^0.7.0;
contract Test(int visible unused a, int visible b, int c) {
contract A(int visible unused a, int visible unused b, int c) {
function funcA(string x, int y) {
require(c == 3);
require(x == "A");
require(y == 1);
}
function funcB(int z) {
require(z == 0);
}
}
contract B(int a, int b, int c) {
function funcA(string x, int y) {
require(a == 1);
require(b == 2);
require(c == 3);
require(x == "B");
require(y == 1);
}
}
}
Note, how an MCP contract, unlike conventional ones, has child contracts and defines two MAST contracts A
and B
.
Each MAST contract have to declare the same contract parameters which MCP contract declares, in the same order, otherwise they will become unspendable. These can be marked to be ignored by using visible unused
keywords, however.
Note that (because of implementation details) unlike conventional contracts, MAST contract have to declare unused parameters first, not last:
contract A(int visible unused a, int b)…
instead of the required ordering for normal contracts:
contract A(int a, int visible unused b)…
Instantiation in SDK
MCP artifacts are a bit different from conventional ones and include the artifacts of all MAST contracts compiled. To create an artifact object you'd need to compile the MCP contract source code with the following snippet to compile from string:
import { compileString } from '@nexscript/nexc';
const mcpArtifact = compileString(source);
or to compile from file:
import { compileFile } from '@nexscript/nexc';
const mcpArtifact = compileFile(path);
Then having an artifact, you can instantiate an MCP contract object:
import {
ElectrumNetworkProvider,
McpContract,
} from '@nexscript/nexscript';
const provider = new ElectrumNetworkProvider();
const mcp = new McpContract(mcpArtifact, [1n, 2n, 3n], { provider });
// send some satoshis to contract address in order to "deploy" it on chain
// await fund(mcp.address, 10000);
await mcp.execute({
contractName: 'A',
functionName: 'funcA',
parameters: ['A', 1n],
}).to(aliceAddress, 1000n).withoutChange().send();