|
| 1 | +# Sandbox Mocked Withdrawals |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +**Sandbox mocked withdrawals** allow teams to simulate the withdrawal flow without actually sending testnet funds. Instead, the system mocks provider calls and responses, enabling safe and comprehensive testing across environments. |
| 6 | + |
| 7 | +> **Note:** This behavior is **enabled by default** but can be customized for partners who require on-chain testnet transactions. |
| 8 | +
|
| 9 | + |
| 10 | +## Why Use Mocked Withdrawals? |
| 11 | + |
| 12 | +Acquiring testnet tokens can be cumbersome and time-consuming, especially when testing at scale. Mocked withdrawals solve this by: |
| 13 | + |
| 14 | +- Removing dependencies on testnet faucets |
| 15 | +- Reducing flakiness and wait times |
| 16 | +- Simplifying QA and development workflows |
| 17 | + |
| 18 | +With this setup, partners can validate flows confidently without real token movement or blockchain interaction. |
| 19 | + |
| 20 | +## Supported Networks |
| 21 | + |
| 22 | +Mocked withdrawals can be enabled or disabled across the following networks: |
| 23 | + |
| 24 | +``` |
| 25 | +Algorand, Arbitrum, Avalanche, Base, BNB Smart Chain, Cardano, Casper, Celo, |
| 26 | +Constellation, Cosmos, Dogecoin, Flare, Hedera, Kaspa, Lukso, Mythos, Near, |
| 27 | +Optimism, Polkadot, Polygon, Solana, Songbird, Stacks, Starknet, Stellar, |
| 28 | +Tezos, Ton, Tron, Xinfin, XRP Ledger |
| 29 | +``` |
| 30 | + |
| 31 | +## Mock Types |
| 32 | + |
| 33 | +| Type | Behavior | |
| 34 | +|-----------------|--------------------------------------------------------------------------| |
| 35 | +| `mock_enabled` | Withdrawal is simulated. No funds are sent. Provider calls are mocked. | |
| 36 | +| `mock_disabled` | Withdrawal proceeds normally using testnet funds. | |
| 37 | + |
| 38 | +## How It Works |
| 39 | + |
| 40 | +The system uses a configuration-based approach to determine when a withdrawal should be mocked. |
| 41 | + |
| 42 | +### Matching Criteria |
| 43 | + |
| 44 | +Mocking behavior can be triggered based on: |
| 45 | + |
| 46 | +- `userId` |
| 47 | +- `organizationId` |
| 48 | +- `currency` |
| 49 | +- `network` |
| 50 | + |
| 51 | +Each property acts as an individual match condition. They can also be combined for more complex rules. |
| 52 | + |
| 53 | +### Example Configuration |
| 54 | + |
| 55 | +```js |
| 56 | +mocked: { |
| 57 | + withdrawal: { |
| 58 | + default: "mock-enabled", |
| 59 | + rules: [ |
| 60 | + { |
| 61 | + context: { |
| 62 | + currency: "BTC", |
| 63 | + network: "bitcoin", |
| 64 | + whitelistedOrganizations: ["org-id-123"], |
| 65 | + whitelistedUsers: ["user-id-456"] |
| 66 | + }, |
| 67 | + id: "rule-1-id", |
| 68 | + mockType: "mock-disabled" |
| 69 | + }, |
| 70 | + { |
| 71 | + context: { |
| 72 | + currency: "ETH", |
| 73 | + whitelistedUsers: ["user-id-456"] |
| 74 | + }, |
| 75 | + id: "rule-2-id", |
| 76 | + mockType: "mock-disabled" |
| 77 | + } |
| 78 | + ] |
| 79 | + } |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +## Rule Priority |
| 84 | + |
| 85 | +When multiple rules apply, the system selects the one with the **highest match score**, calculated as: |
| 86 | + |
| 87 | +- Each matching field (user, org, network, currency) = 1 point |
| 88 | +- Highest total wins |
| 89 | +- Ties are resolved by the rule's position (first one wins) |
| 90 | + |
| 91 | +> **Example:** Rule 2 will disable mocks for `user-id-456` when withdrawing ETH. |
| 92 | +
|
| 93 | + |
| 94 | +## Mock Service |
| 95 | + |
| 96 | +The `mock-service` determines whether a function should be mocked based on: |
| 97 | + |
| 98 | +- The current environment (mocks are **never** used in production) |
| 99 | +- The `mockType` value |
| 100 | + |
| 101 | +### Core API |
| 102 | + |
| 103 | +```ts |
| 104 | +static async mockIfRequired(mockType = MOCK_ENABLED, { |
| 105 | + executionCallback, |
| 106 | + mockedCallback |
| 107 | +}) { |
| 108 | + if (config.get('env') !== 'production') { |
| 109 | + if (mockType === MOCK_ENABLED) { |
| 110 | + return mockedCallback(); |
| 111 | + } |
| 112 | + } |
| 113 | + return await executionCallback(); |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +> It also includes logic to resolve priority between rules. |
| 118 | +
|
| 119 | +## Example: Mocked Nonce Call |
| 120 | + |
| 121 | +```ts |
| 122 | +const nonce = await MockService.mockIfRequired(mockType, { |
| 123 | + executionCallback: async () => |
| 124 | + await this.addressSequenceHelper.getAddressSequence(originAddress), |
| 125 | + mockedCallback: () => 0 |
| 126 | +}); |
| 127 | +``` |
| 128 | + |
| 129 | +In this example, the nonce value is mocked when the rule applies, allowing the rest of the flow to continue without relying on the blockchain. |
| 130 | + |
| 131 | +## References |
| 132 | + |
| 133 | +- [Mock Types](#mock-types) |
| 134 | +- [How It Works](#how-it-works) |
| 135 | +- [Supported Networks](#supported-networks) |
0 commit comments