> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tenderly.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Simulation Bundles with SDK

> जानें कि Tenderly SDK का उपयोग करके Simulation Bundles के साथ एक transaction bundle कैसे निष्पादित करें।

इस ट्यूटोरियल में, आप सीखेंगे कि bundled transactions को simulate करने के लिए Tenderly SDK का उपयोग कैसे करें। एक bundled transaction कई transactions का एक संग्रह है जिन्हें एक इकाई के रूप में simulate किया जाता है।

Bundled transactions का उपयोग अक्सर DeFi protocols और अन्य एप्लिकेशन में जटिल ऑपरेशन्स करने की प्रक्रिया को simulate करने के लिए किया जाता है जिनमें कई contract इंटरैक्शन शामिल होते हैं।

<Note>एक bundle से transactions एक ही blocks पर simulate किए जाते हैं।</Note>

हम DAI की एक विशेष राशि को WETH के लिए स्वैप करने के लिए Uniswap के उपयोग को simulate करेंगे, और simulate किए गए transactions की निष्पादन स्थिति दिखाएंगे, और उपयोग की गई कुल gas की गणना करेंगे।

इसे प्राप्त करने के लिए आवश्यक transactions इस प्रकार हैं:

1. **2 DAI के minting को simulate करें** ताकि स्वैपर के पास स्वैप करने के लिए कुछ संपत्ति हो। वैकल्पिक रूप से, यदि आपके पास DAI है, तो आप इसे छोड़ सकते हैं और सिर्फ transaction 2 कर सकते हैं।
2. **UniswapV3Router को** DAI का उपयोग करने के लिए **Approve करें**।
3. **स्वैप करें**। स्वैप करने के लिए `UniswapV2Router.exactInputSingle` को कॉल करें।

पहले transaction को सफलतापूर्वक चलाने के लिए, प्रेषक को DAI stablecoin का ward होना आवश्यक है। चूंकि हम में से अधिकांश नहीं हैं, हम देखेंगे कि Bundled Simulation के संदर्भ में "ward बनने" के लिए State Overrides का उपयोग कैसे करें।

### आवश्यक शर्तें

* आपकी मशीन पर Node.js इंस्टॉल किया गया हो
* [एक API key के साथ Tenderly अकाउंट](/platform/account/projects/api-tokens)
* [Tenderly Dashboard में सेट अप किया गया Project](/platform/account/projects/slug)

### चरण 1: Project सेट अप करें

एक नया npm project बनाएं, निर्भरताएं इंस्टॉल करें, और अपने टर्मिनल में निम्नलिखित कमांड पेस्ट करके Typescript कॉन्फ़िगर करें:

```bash title="example" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
mkdir tenderly-sdk-simulations
cd tenderly-sdk-simulations
npm init -y
yarn add @tenderly/sdk ethers
yarn add --dev typescript ts-node @types/node  dotenv
echo '{
  "compilerOptions": {
  "module": "commonjs",
  "esModuleInterop": true,
  "target": "es6",
  "moduleResolution": "node",
  "sourceMap": true,
  "outDir": "dist"
  "moduleResolution": "node16",
  },
  "lib": ["es2015"]
}' > tsconfig.json

mkdir src
touch src/swap.ts
code .
```

### चरण 2: Simulation API को कॉल करें

नीचे दिए गए पूर्ण चलाने योग्य उदाहरण को कॉपी करें और इसे `swap.ts` फ़ाइल में पेस्ट करें।

Transactions को functions `mint2DaiTx`, `approveUniswapV3RouterTx`, और `swapSomeDaiForWethTx` द्वारा निर्धारित किया जाता है:

```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}

const fakeWardAddressEOA = '0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2';
const daiOwnerEOA = '0xe58b9ee93700a616b50509c8292977fa7a0f8ce1';
const daiAddressMainnet = '0x6b175474e89094c44da98b954eedeac495271d0f';
const uniswapV3SwapRouterAddressMainnet = '0xe592427a0aece92de3edee1f18e0157c05861564';
const wethAddressMainnet = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';

(async () => {
  const tenderly = new Tenderly({
    accessKey: 'access key',
    accountName: 'your account name',
    projectName: 'project-name-hyphenated',
    network: Network.MAINNET,
  });

  const simulatedBundle = await tenderly.simulator.simulateBundle({
    blockNumber: 0x103a957,
    transactions: [
      // TX1: Mint 2 DAI for daiOwnerEOA.
      // For minting to happen, we must do a state override so fakeWardAddress EOA is considered a ward for this simulation (see overrides)
      mint2DaiTx(),
      // TX2: daiOwnerEOA approves 1 DAI to uniswapV3SwapRouterAddressMainnet
      approveUniswapV3RouterTx(),
      // TX3: Perform a uniswap swap of 1/3 ETH
      swapSomeDaiForWethTx(),
    ],
    overrides: {
      [daiAddressMainnet]: {
        state: {
          // make DAI think that fakeWardAddress is a ward for minting
          [`wards[${fakeWardAddressEOA}]`]:
            '0x0000000000000000000000000000000000000000000000000000000000000001',
        },
      },
    },
  });
  const totalGasUsed = simulatedBundle
    .map(simulation => simulation.gasUsed)
    .reduce((total, gasUsed) => total + gasUsed);

  console.log('Total gas used:', totalGasUsed);

  simulatedBundle.forEach((simulation, idx) => {
    console.log(
      `Transaction ${idx} at block ${simulation.blockNumber}`,
      simulation.status ? 'success' : 'failed',
    );
  });

  console.log(JSON.stringify(simulatedBundle, null, 2));
})();

function mint2DaiTx(): TransactionParameters {
  return {
    from: fakeWardAddressEOA,
    to: daiAddressMainnet,
    gas: 0,
    gas_price: '0',
    value: 0,
    input: daiEthersInterface().encodeFunctionData('mint', [daiOwnerEOA, parseEther('2')]),
  };
}

function approveUniswapV3RouterTx(): TransactionParameters {
  return {
    from: daiOwnerEOA,
    to: daiAddressMainnet,
    gas: 0,
    gas_price: '0',
    value: 0,
    input: daiEthersInterface().encodeFunctionData('approve', [
      uniswapV3SwapRouterAddressMainnet,
      parseEther('1'),
    ]),
  };
}

function swapSomeDaiForWethTx(): TransactionParameters {
  return {
    from: daiOwnerEOA,
    to: uniswapV3SwapRouterAddressMainnet,
    gas: 0,
    gas_price: '0',
    value: 0,
    input: uniswapRouterV2EthersInterface().encodeFunctionData('exactInputSingle', [
      {
        tokenIn: daiAddressMainnet,
        tokenOut: wethAddressMainnet,
        fee: '10000',
        recipient: daiOwnerEOA,
        deadline: (1681109951 + 10 * 365 * 24 * 60 * 60 * 1000).toString(),
        amountIn: '33000000000000000',
        amountOutMinimum: '763124874493',
        sqrtPriceLimitX96: '0',
      },
    ]),
  };
}

function daiEthersInterface() {
  // @formatter:off
  const daiAbi=[
    {constant:false,inputs:[{internalType:'address',name:'src',type:'address',},{internalType:'address',name:'dst',type:'address',},{internalType:'uint256',name:'wad',type:'uint256',},],name:'transferFrom',outputs:[{internalType:'bool',name:'',type:'bool',},],payable:false,stateMutability:'nonpayable',type:'function',},
    {constant:false,inputs:[{internalType:'address',name:'usr',type:'address',},{internalType:'uint256',name:'wad',type:'uint256',},],name:'approve',outputs:[{internalType:'bool',name:'',type:'bool',},],payable:false,stateMutability:'nonpayable',type:'function',},
    {constant:false,inputs:[{internalType:'address',name:'usr',type:'address',},{internalType:'uint256',name:'wad',type:'uint256',},],name:'mint',outputs:[],payable:false,stateMutability:'nonpayable',type:'function',}
  ];

  return new Interface(daiAbi);
  // @formatter:on
}

function uniswapRouterV2EthersInterface() {
  // @formatter:off
  const swapRouterAbi = [
    {inputs: [ {internalType: 'address', name: '_factory', type: 'address',}, {internalType: 'address', name: '_WETH9', type: 'address',}, ], stateMutability: 'nonpayable', type: 'constructor',},
    {inputs: [], name: 'WETH9', outputs: [ {internalType: 'address', name: '', type: 'address',}, ], stateMutability: 'view', type: 'function',},
    {inputs: [ {components: [ {internalType: 'bytes', name: 'path', type: 'bytes',}, {internalType: 'address', name: 'recipient', type: 'address',}, {internalType: 'uint256', name: 'deadline', type: 'uint256',}, {internalType: 'uint256', name: 'amountIn', type: 'uint256',}, {internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256',}, ], internalType: 'struct ISwapRouter.ExactInputParams', name: 'params', type: 'tuple',}, ], name: 'exactInput', outputs: [ {internalType: 'uint256', name: 'amountOut', type: 'uint256',}, ], stateMutability: 'payable', type: 'function',},
    {inputs: [ {components: [ {internalType: 'address', name: 'tokenIn', type: 'address',}, {internalType: 'address', name: 'tokenOut', type: 'address',}, {internalType: 'uint24', name: 'fee', type: 'uint24',}, {internalType: 'address', name: 'recipient', type: 'address',}, {internalType: 'uint256', name: 'deadline', type: 'uint256',}, {internalType: 'uint256', name: 'amountIn', type: 'uint256',}, {internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256',}, {internalType: 'uint160', name: 'sqrtPriceLimitX96', type: 'uint160',}, ], internalType: 'struct ISwapRouter.ExactInputSingleParams', name: 'params', type: 'tuple',}, ], name: 'exactInputSingle', outputs: [ {internalType: 'uint256', name: 'amountOut', type: 'uint256',}, ], stateMutability: 'payable', type: 'function',},
    {inputs: [ {components: [ {internalType: 'bytes', name: 'path', type: 'bytes',}, {internalType: 'address', name: 'recipient', type: 'address',}, {internalType: 'uint256', name: 'deadline', type: 'uint256',}, {internalType: 'uint256', name: 'amountOut', type: 'uint256',}, {internalType: 'uint256', name: 'amountInMaximum', type: 'uint256',}, ], internalType: 'struct ISwapRouter.ExactOutputParams', name: 'params', type: 'tuple',}, ], name: 'exactOutput', outputs: [ {internalType: 'uint256', name: 'amountIn', type: 'uint256',}, ], stateMutability: 'payable', type: 'function',},
    {inputs: [ {components: [ {internalType: 'address', name: 'tokenIn', type: 'address',}, {internalType: 'address', name: 'tokenOut', type: 'address',}, {internalType: 'uint24', name: 'fee', type: 'uint24',}, {internalType: 'address', name: 'recipient', type: 'address',}, {internalType: 'uint256', name: 'deadline', type: 'uint256',}, {internalType: 'uint256', name: 'amountOut', type: 'uint256',}, {internalType: 'uint256', name: 'amountInMaximum', type: 'uint256',}, {internalType: 'uint160', name: 'sqrtPriceLimitX96', type: 'uint160',}, ], internalType: 'struct ISwapRouter.ExactOutputSingleParams', name: 'params', type: 'tuple',}, ], name: 'exactOutputSingle', outputs: [ {internalType: 'uint256', name: 'amountIn', type: 'uint256',}, ], stateMutability: 'payable', type: 'function',},
    {inputs: [], name: 'factory', outputs: [ {internalType: 'address', name: '', type: 'address',}, ], stateMutability: 'view', type: 'function',},
    {inputs: [ {internalType: 'bytes[]', name: 'data', type: 'bytes[]',}, ], name: 'multicall', outputs: [ {internalType: 'bytes[]', name: 'results', type: 'bytes[]',}, ], stateMutability: 'payable', type: 'function',},
    {inputs: [], name: 'refundETH', outputs: [], stateMutability: 'payable', type: 'function',},
    {inputs: [ {internalType: 'address', name: 'token', type: 'address',}, {internalType: 'uint256', name: 'value', type: 'uint256',}, {internalType: 'uint256', name: 'deadline', type: 'uint256',}, {internalType: 'uint8', name: 'v', type: 'uint8',}, {internalType: 'bytes32', name: 'r', type: 'bytes32',}, {internalType: 'bytes32', name: 's', type: 'bytes32',}, ], name: 'selfPermit', outputs: [], stateMutability: 'payable', type: 'function',},
    {inputs: [ {internalType: 'address', name: 'token', type: 'address',}, {internalType: 'uint256', name: 'nonce', type: 'uint256',}, {internalType: 'uint256', name: 'expiry', type: 'uint256',}, {internalType: 'uint8', name: 'v', type: 'uint8',}, {internalType: 'bytes32', name: 'r', type: 'bytes32',}, {internalType: 'bytes32', name: 's', type: 'bytes32',}, ], name: 'selfPermitAllowed', outputs: [], stateMutability: 'payable', type: 'function',},
    {inputs: [ {internalType: 'address', name: 'token', type: 'address',}, {internalType: 'uint256', name: 'nonce', type: 'uint256',}, {internalType: 'uint256', name: 'expiry', type: 'uint256',}, {internalType: 'uint8', name: 'v', type: 'uint8',}, {internalType: 'bytes32', name: 'r', type: 'bytes32',}, {internalType: 'bytes32', name: 's', type: 'bytes32',}, ], name: 'selfPermitAllowedIfNecessary', outputs: [], stateMutability: 'payable', type: 'function',},
    {inputs: [ {internalType: 'address', name: 'token', type: 'address',}, {internalType: 'uint256', name: 'value', type: 'uint256',}, {internalType: 'uint256', name: 'deadline', type: 'uint256',}, {internalType: 'uint8', name: 'v', type: 'uint8',}, {internalType: 'bytes32', name: 'r', type: 'bytes32',}, {internalType: 'bytes32', name: 's', type: 'bytes32',}, ], name: 'selfPermitIfNecessary', outputs: [], stateMutability: 'payable', type: 'function',},
    {inputs: [ {internalType: 'address', name: 'token', type: 'address',}, {internalType: 'uint256', name: 'amountMinimum', type: 'uint256',}, {internalType: 'address', name: 'recipient', type: 'address',}, ], name: 'sweepToken', outputs: [], stateMutability: 'payable', type: 'function',},
    {inputs: [ {internalType: 'address', name: 'token', type: 'address',}, {internalType: 'uint256', name: 'amountMinimum', type: 'uint256',}, {internalType: 'address', name: 'recipient', type: 'address',}, {internalType: 'uint256', name: 'feeBips', type: 'uint256',}, {internalType: 'address', name: 'feeRecipient', type: 'address',}, ], name: 'sweepTokenWithFee', outputs: [], stateMutability: 'payable', type: 'function',},
    {inputs: [ {internalType: 'int256', name: 'amount0Delta', type: 'int256',}, {internalType: 'int256', name: 'amount1Delta', type: 'int256',}, {internalType: 'bytes', name: '_data', type: 'bytes',}, ], name: 'uniswapV3SwapCallback', outputs: [], stateMutability: 'nonpayable', type: 'function',}, {inputs: [ {internalType: 'uint256', name: 'amountMinimum', type: 'uint256',}, {internalType: 'address', name: 'recipient', type: 'address',}, ], name: 'unwrapWETH9', outputs: [], stateMutability: 'payable', type: 'function',}, {inputs: [ {internalType: 'uint256', name: 'amountMinimum', type: 'uint256',}, {internalType: 'address', name: 'recipient', type: 'address',}, {internalType: 'uint256', name: 'feeBips', type: 'uint256',}, {internalType: 'address', name: 'feeRecipient', type: 'address',}, ], name: 'unwrapWETH9WithFee', outputs: [], stateMutability: 'payable', type: 'function',}, {stateMutability: 'payable', type: 'receive',}
  ];

  return new Interface(swapRouterAbi);
  // @formatter:on
}
```

ट्यूटोरियल में आगे, आप इस बात के विस्तृत स्पष्टीकरण पाएंगे कि कोड क्या कर रहा है।

### चरण 3: स्क्रिप्ट चलाएं

```bash title="example" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
npx ts-node src/swap.ts
```

### कोड को समझना

पहले, हम Tenderly SDK का एक नया इंस्टेंस बना रहे हैं:

```typescript title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
const tenderly = new Tenderly({
  accessKey: 'access key',
  accountName: 'your account name',
  projectName: 'project-name-hyphenated',
  network: Network.MAINNET,
});
```

Simulation API को कॉल करने और एक bundled simulation करने के लिए, हम `Tenderly.simulator.simulateBundle()` का उपयोग कर रहे हैं और निम्नलिखित फ़ील्ड्स वाला एक object पास कर रहे हैं:

* `blockNumber` - वह block number जिस पर आप simulation को निष्पादित करना चाहते हैं।
* `transactions` - तीन transactions रखने वाली एक array: 2 DAI का minting, Uniswap Router की approving, और WETH के लिए DAI की swapping।
* `overrides` - एक object जो simulation के भीतर `fakeWardAddress` को DAI का ward बनाता है।

```typescript title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
const simulatedBundle = await tenderly.simulator.simulateBundle({
  blockNumber: 0x103a957,
  transactions: [
    // TX1: Mint 2 DAI for daiOwnerEOA.
    // For minting to happen, we must do a state override so fakeWardAddressEOA is considered a ward for this simulation (see overrides below)
    mint2DaiTx(),
    // TX2: daiOwnerEOA approves 1 DAI to uniswapV3SwapRouterAddressMainnet
    approveUniswapV3RouterTx(),
    // TX3: Perform a uniswap swap of 1/3 ETH
    swapSomeDaiForWethTx(),
  ],
  overrides: {
    [daiAddressMainnet]: {
      state: {
        // make DAI think that fakeWardAddress is a ward for minting
        [`wards[${fakeWardAddressEOA}]`]:
          '0x0000000000000000000000000000000000000000000000000000000000000001',
      },
    },
  },
});
```

### State Overrides को समझना

हम DAI stablecoin के `mint` function को कॉल कर रहे हैं। simulation के दौरान, DAI contract को यह विश्वास करना होगा कि आप (प्रेषक) wards में से एक हैं। अन्यथा, यह mint करने से इंकार कर देगा।

`fakeWardAddressEOA` द्वारा शुरू किए गए एक सफल minting के लिए DAI contract के state के लिए हमें निम्नलिखित शर्त को बनाए रखना होगा:

```typescript title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
daiAddressMainnet.state.wards[fakeWardAddressEOA] == 0x0000...0001
```

इसे सक्षम करने के लिए, हम SDK को `overrides` पास कर रहे हैं, जहां हम DAI `wards` storage variable को override कर रहे हैं, ताकि simulation के दौरान `fakeDaiWardAddress` एक ward बन जाए।

```typescript title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
overrides: {
  [daiAddressMainnet]: {
    state: {
        // make DAI think that fakeWardAddress is a ward for minting
        [`wards[${fakeWardAddressEOA}]`]:
        '0x0000000000000000000000000000000000000000000000000000000000000001',
    },
  },
},
```

<Note>
  ध्यान दें कि overrides **पूरे bundle** पर लागू होते हैं और bundle के भीतर सभी simulations के लिए दृश्यमान होते हैं।
</Note>

अब, आपको जटिल simulation परिदृश्यों को करने और आवश्यक परिणामों को प्रदर्शित करने के लिए Tenderly SDK का उपयोग करने की बेहतर समझ है।
