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 { SimulationBuilder } from '..';
SimulationBuilder.new()
const test = () => SimulationBuilder.new()
.withSender('SP212Y5JKN59YP3GYG07K3S8W5SSGE4KH6B5STXER')
.inlineSimulation('1ab04a8d13d72a301b77b6af9d4f612b')
.addContractDeploy({
contract_name: 'test-simulation',
source_code: `
@@ -37,46 +38,9 @@ SimulationBuilder.new()
'(get-counter)'
)
.run()
.catch(console.error);
SimulationBuilder.new({
apiEndpoint: 'https://testnet-api.stxer.xyz',
stacksNodeAPI: 'https://api.testnet.hiro.so',
network: 'testnet',
})
.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);
if (require.main === module) {
; (async () => {
await test()
})().catch(console.error);
}

View File

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

View File

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