feat: enhance SimulationBuilder with inline simulation support and refactor sample usage

- Added `inlineSimulation` method to `SimulationBuilder` for handling inline simulations.
- Updated the `steps` structure to include a `simulationId` for inline simulations.
- Refactored sample code in `counter.ts` to demonstrate the new inline simulation feature.
- Modified `read.ts` to ensure the main function runs correctly when the module is executed directly.
This commit is contained in:
Kyle Fang
2025-01-21 03:18:08 +00:00
parent 33b109b398
commit fc97c2e0f1
3 changed files with 66 additions and 78 deletions

View File

@@ -1,8 +1,9 @@
import { uintCV } from '@stacks/transactions'; import { uintCV } from '@stacks/transactions';
import { SimulationBuilder } from '..'; import { SimulationBuilder } from '..';
SimulationBuilder.new() const test = () => SimulationBuilder.new()
.withSender('SP212Y5JKN59YP3GYG07K3S8W5SSGE4KH6B5STXER') .withSender('SP212Y5JKN59YP3GYG07K3S8W5SSGE4KH6B5STXER')
.inlineSimulation('1ab04a8d13d72a301b77b6af9d4f612b')
.addContractDeploy({ .addContractDeploy({
contract_name: 'test-simulation', contract_name: 'test-simulation',
source_code: ` source_code: `
@@ -37,46 +38,9 @@ SimulationBuilder.new()
'(get-counter)' '(get-counter)'
) )
.run() .run()
.catch(console.error);
SimulationBuilder.new({ if (require.main === module) {
apiEndpoint: 'https://testnet-api.stxer.xyz', ; (async () => {
stacksNodeAPI: 'https://api.testnet.hiro.so', await test()
network: 'testnet', })().catch(console.error);
}) }
.withSender('ST3MZM9WJ34Y4311XBJDBKQ41SXX5DY68406J26WJ')
.addContractDeploy({
contract_name: 'test-simulation',
source_code: `
;; counter example
(define-data-var counter uint u0)
(define-public (increment (delta uint))
(begin
(var-set counter (+ (var-get counter) delta))
(ok (var-get counter))))
(define-public (decrement)
(begin
(var-set counter (- (var-get counter) u1))
(ok (var-get counter))))
(define-read-only (get-counter)
(ok (var-get counter)))
`,
})
.addEvalCode(
'ST3MZM9WJ34Y4311XBJDBKQ41SXX5DY68406J26WJ.test-simulation',
'(get-counter)'
)
.addContractCall({
contract_id: 'ST3MZM9WJ34Y4311XBJDBKQ41SXX5DY68406J26WJ.test-simulation',
function_name: 'increment',
function_args: [uintCV(10)],
})
.addEvalCode(
'ST3MZM9WJ34Y4311XBJDBKQ41SXX5DY68406J26WJ.test-simulation',
'(get-counter)'
)
.run()
.catch(console.error);

View File

@@ -121,5 +121,7 @@ import {
await batchReadonlyExample(); await batchReadonlyExample();
} }
main().catch(console.error); if (require.main === module) {
main().catch(console.error);
}

View File

@@ -12,6 +12,7 @@ import {
type StacksTransactionWire, type StacksTransactionWire,
bufferCV, bufferCV,
contractPrincipalCV, contractPrincipalCV,
deserializeTransaction,
makeUnsignedContractCall, makeUnsignedContractCall,
makeUnsignedContractDeploy, makeUnsignedContractDeploy,
makeUnsignedSTXTokenTransfer, makeUnsignedSTXTokenTransfer,
@@ -139,28 +140,32 @@ export class SimulationBuilder {
private sender = ''; private sender = '';
private steps: ( private steps: (
| { | {
// contract call // inline simulation
contract_id: string; simulationId: string;
function_name: string; }
function_args?: ClarityValue[];
sender: string;
fee: number;
}
| { | {
// contract deploy // contract call
contract_name: string; contract_id: string;
source_code: string; function_name: string;
deployer: string; function_args?: ClarityValue[];
fee: number; sender: string;
clarity_version: ClarityVersion; fee: number;
} }
| { | {
// STX transfer // contract deploy
recipient: string; contract_name: string;
amount: number; source_code: string;
sender: string; deployer: string;
fee: number; fee: number;
} clarity_version: ClarityVersion;
}
| {
// STX transfer
recipient: string;
amount: number;
sender: string;
fee: number;
}
| SimulationEval | SimulationEval
)[] = []; )[] = [];
@@ -172,6 +177,12 @@ export class SimulationBuilder {
this.sender = address; this.sender = address;
return this; return this;
} }
public inlineSimulation(simulationId: string) {
this.steps.push({
simulationId,
})
return this;
}
public addSTXTransfer(params: { public addSTXTransfer(params: {
recipient: string; recipient: string;
amount: number; amount: number;
@@ -298,9 +309,8 @@ To get in touch: contact@stxer.xyz
const nextNonce = async (sender: string) => { const nextNonce = async (sender: string) => {
const nonce = nonce_by_address.get(sender); const nonce = nonce_by_address.get(sender);
if (nonce == null) { if (nonce == null) {
const url = `${ const url = `${this.stacksNodeAPI
this.stacksNodeAPI }/v2/accounts/${sender}?proof=${false}&tip=${block.index_block_hash}`;
}/v2/accounts/${sender}?proof=${false}&tip=${block.index_block_hash}`;
const account: AccountDataResponse = await richFetch(url).then((r) => const account: AccountDataResponse = await richFetch(url).then((r) =>
r.json() r.json()
); );
@@ -310,18 +320,30 @@ To get in touch: contact@stxer.xyz
nonce_by_address.set(sender, nonce + 1); nonce_by_address.set(sender, nonce + 1);
return nonce; return nonce;
}; };
let network = this.network === 'mainnet' ? STACKS_MAINNET : STACKS_TESTNET;
if (this.stacksNodeAPI) {
network = {
...network,
client: {
...network.client,
baseUrl: this.stacksNodeAPI,
},
};
}
for (const step of this.steps) { for (const step of this.steps) {
let network = this.network === 'mainnet' ? STACKS_MAINNET : STACKS_TESTNET; if ('simulationId' in step) {
if (this.stacksNodeAPI) { const previousSimulation: {steps: ({tx: string} | {code: string, contract: string})[]} = await fetch(`https://api.stxer.xyz/simulations/${step.simulationId}/request`).then(x => x.json())
network = { for (const step of previousSimulation.steps) {
...network, if ('tx' in step) {
client: { txs.push(deserializeTransaction(step.tx));
...network.client, } else if ('code' in step && 'contract' in step) {
baseUrl: this.stacksNodeAPI, txs.push({
}, contract_id: step.contract,
}; code: step.code,
} });
if ('sender' in step && 'function_name' in step) { }
}
} else if ('sender' in step && 'function_name' in step) {
const nonce = await nextNonce(step.sender); const nonce = await nextNonce(step.sender);
const [contractAddress, contractName] = step.contract_id.split('.'); const [contractAddress, contractName] = step.contract_id.split('.');
const tx = await makeUnsignedContractCall({ const tx = await makeUnsignedContractCall({