643 lines
19 KiB
Markdown
643 lines
19 KiB
Markdown
# CoW Protocol Integration: Implementation Checklist & Testing Guide
|
|
|
|
## Phase 1: Contract Setup & Verification (3-5 days)
|
|
|
|
### 1.1 Bonding Curve Validation
|
|
- [ ] **MycoBondingCurve.sol**
|
|
- [ ] Verify polynomial pricing formula (basePrice + coeff * supply^exponent)
|
|
- [ ] Test price calculation at various supply levels
|
|
- [ ] Confirm buy/sell mechanics preserve reserve invariant
|
|
- [ ] Validate fee calculation (grossAmount - (grossAmount * feePercentage / 10000))
|
|
- [ ] Check that calculateBuyReturn() and calculateSellReturn() are accurate
|
|
- [ ] Gas optimization: profile buy/sell gas usage
|
|
|
|
- [ ] **MycoToken.sol**
|
|
- [ ] Verify mint() only callable by bonding curve
|
|
- [ ] Verify burnFrom() works with and without allowance
|
|
- [ ] Test that total supply updates correctly
|
|
- [ ] Confirm token decimals = 18
|
|
|
|
### 1.2 Adapter Validation
|
|
- [ ] **BondingCurveAdapter.sol**
|
|
- [ ] Verify `onlySettlement` modifier protection
|
|
- [ ] Test executeBuyForCoW():
|
|
- [ ] Receives USDC correctly from settlement
|
|
- [ ] Mints MYCO to adapter
|
|
- [ ] Reverts if insufficient balance
|
|
- [ ] Respects minMycoOut slippage protection
|
|
- [ ] Test executeSellForCoW():
|
|
- [ ] Receives MYCO correctly from settlement
|
|
- [ ] Burns MYCO correctly
|
|
- [ ] Respects minUsdcOut slippage protection
|
|
- [ ] Updates adapter balance after sale
|
|
- [ ] Test quoteBuy() and quoteSell():
|
|
- [ ] Returns expected values
|
|
- [ ] Matches calculateBuyReturn/calculateSellReturn
|
|
- [ ] No side effects (view function)
|
|
|
|
- [ ] **BondingCurveAdapter Pre-approvals**
|
|
- [ ] Verify pre-approval to bonding curve (USDC & MYCO)
|
|
- [ ] Verify pre-approval to vault relayer (USDC & MYCO)
|
|
- [ ] Test that settlements can pull tokens via vault relayer
|
|
|
|
### 1.3 Conditional Order Validation
|
|
- [ ] **MycoConditionalOrder.sol**
|
|
- [ ] Verify IConditionalOrder interface implementation
|
|
- [ ] Test getTradeableOrder() with:
|
|
- [ ] Valid staticInput (buy order)
|
|
- [ ] Valid staticInput (sell order)
|
|
- [ ] Expired quote (should revert OrderNotValid)
|
|
- [ ] Quote below minimum (should revert OrderNotValid)
|
|
- [ ] Insufficient user balance (should revert PollTryNextBlock)
|
|
- [ ] Test verify() function:
|
|
- [ ] Calls getTradeableOrder internally
|
|
- [ ] Doesn't modify state
|
|
- [ ] Reverts if order invalid
|
|
- [ ] Verify APP_DATA constant uniqueness
|
|
|
|
### 1.4 Interface Compliance
|
|
- [ ] **IConditionalOrder.sol**
|
|
- [ ] All error types defined
|
|
- [ ] Function signatures match standard
|
|
- [ ] Documentation complete
|
|
|
|
- [ ] **GPv2Order.sol**
|
|
- [ ] Order struct correct
|
|
- [ ] KIND_SELL, KIND_BUY constants defined
|
|
- [ ] BALANCE_ERC20 constant defined
|
|
- [ ] hash() function correct for EIP-712
|
|
|
|
---
|
|
|
|
## Phase 2: Unit Testing (5-7 days)
|
|
|
|
### 2.1 Bonding Curve Tests
|
|
```solidity
|
|
// File: contracts/tests/MycoBondingCurve.test.ts
|
|
|
|
test("buy increases price correctly", async () => {
|
|
const initialPrice = await curve.getCurrentPrice();
|
|
const supply = await mycoToken.totalSupply();
|
|
|
|
await usdc.transfer(user.address, ethers.parseUnits("1000", 6));
|
|
await usdc.connect(user).approve(curve.address, ethers.parseUnits("1000", 6));
|
|
|
|
const receipt = await curve.connect(user).buy(
|
|
ethers.parseUnits("500", 6), // 500 USDC
|
|
ethers.parseUnits("900", 18) // 900 MYCO min
|
|
);
|
|
|
|
const newPrice = await curve.getCurrentPrice();
|
|
expect(newPrice).to.be.gt(initialPrice);
|
|
|
|
// Verify: price(newSupply) > price(oldSupply)
|
|
});
|
|
|
|
test("sell decreases price correctly", async () => {
|
|
// Setup: first buy some tokens
|
|
await curve.connect(user).buy(
|
|
ethers.parseUnits("500", 6),
|
|
ethers.parseUnits("900", 18)
|
|
);
|
|
|
|
const beforePrice = await curve.getCurrentPrice();
|
|
const beforeSupply = await mycoToken.totalSupply();
|
|
|
|
// Now sell
|
|
await mycoToken.connect(user).approve(curve.address, ethers.parseUnits("100", 18));
|
|
await curve.connect(user).sell(
|
|
ethers.parseUnits("100", 18), // 100 MYCO
|
|
ethers.parseUnits("50", 6) // 50 USDC min
|
|
);
|
|
|
|
const afterPrice = await curve.getCurrentPrice();
|
|
const afterSupply = await mycoToken.totalSupply();
|
|
|
|
expect(afterPrice).to.be.lt(beforePrice);
|
|
expect(afterSupply).to.be.eq(beforeSupply - ethers.parseUnits("100", 18));
|
|
});
|
|
|
|
test("slippage protection works", async () => {
|
|
await usdc.transfer(user.address, ethers.parseUnits("1000", 6));
|
|
await usdc.connect(user).approve(curve.address, ethers.parseUnits("1000", 6));
|
|
|
|
const quote = await curve.calculateBuyReturn(ethers.parseUnits("500", 6));
|
|
const tooHigh = quote + ethers.parseUnits("1", 18);
|
|
|
|
await expect(
|
|
curve.connect(user).buy(
|
|
ethers.parseUnits("500", 6),
|
|
tooHigh // Request more than possible
|
|
)
|
|
).to.be.revertedWithCustomError(curve, "SlippageExceeded");
|
|
});
|
|
|
|
test("reserve balance tracks USDC correctly", async () => {
|
|
const initialReserve = await curve.reserveBalance();
|
|
|
|
await usdc.transfer(user.address, ethers.parseUnits("500", 6));
|
|
await usdc.connect(user).approve(curve.address, ethers.parseUnits("500", 6));
|
|
|
|
await curve.connect(user).buy(
|
|
ethers.parseUnits("500", 6),
|
|
ethers.parseUnits("900", 18)
|
|
);
|
|
|
|
const newReserve = await curve.reserveBalance();
|
|
expect(newReserve).to.equal(initialReserve + ethers.parseUnits("500", 6));
|
|
});
|
|
|
|
test("fee calculation correct", async () => {
|
|
// Setup: create positions
|
|
await curve.updateFeePercentage(500); // 5% fee
|
|
|
|
const quote = await curve.calculateSellReturn(ethers.parseUnits("1000", 18));
|
|
|
|
// quote = (grossAmount * (10000 - 500)) / 10000
|
|
// Verify: quote < grossAmount
|
|
});
|
|
```
|
|
|
|
### 2.2 Adapter Tests
|
|
```solidity
|
|
// File: contracts/tests/BondingCurveAdapter.test.ts
|
|
|
|
test("executeBuyForCoW receives USDC and mints MYCO", async () => {
|
|
// Simulate settlement sending USDC
|
|
await usdc.transfer(adapter.address, ethers.parseUnits("500", 6));
|
|
|
|
// Call as if from settlement
|
|
const tx = await adapter.connect(settlement).executeBuyForCoW(
|
|
ethers.parseUnits("500", 6),
|
|
ethers.parseUnits("900", 18)
|
|
);
|
|
|
|
const receipt = await tx.wait();
|
|
expect(receipt?.logs.length).to.be.gt(0);
|
|
|
|
const mycoBalance = await mycoToken.balanceOf(adapter.address);
|
|
expect(mycoBalance).to.be.gte(ethers.parseUnits("900", 18));
|
|
});
|
|
|
|
test("executeBuyForCoW reverts if not called by settlement", async () => {
|
|
await usdc.transfer(adapter.address, ethers.parseUnits("500", 6));
|
|
|
|
await expect(
|
|
adapter.connect(user).executeBuyForCoW(
|
|
ethers.parseUnits("500", 6),
|
|
ethers.parseUnits("900", 18)
|
|
)
|
|
).to.be.revertedWithCustomError(adapter, "OnlySettlement");
|
|
});
|
|
|
|
test("quoteBuy returns correct value", async () => {
|
|
const quote = await adapter.quoteBuy(ethers.parseUnits("500", 6));
|
|
const expected = await curve.calculateBuyReturn(ethers.parseUnits("500", 6));
|
|
|
|
expect(quote).to.equal(expected);
|
|
});
|
|
|
|
test("quoteSell returns correct value", async () => {
|
|
// Create some supply first
|
|
await curve.connect(user).buy(
|
|
ethers.parseUnits("500", 6),
|
|
ethers.parseUnits("900", 18)
|
|
);
|
|
|
|
const quote = await adapter.quoteSell(ethers.parseUnits("100", 18));
|
|
const expected = await curve.calculateSellReturn(ethers.parseUnits("100", 18));
|
|
|
|
expect(quote).to.equal(expected);
|
|
});
|
|
|
|
test("reentrancy guard prevents reentry", async () => {
|
|
// Deploy malicious contract that tries to reenter
|
|
// Verify revert
|
|
});
|
|
```
|
|
|
|
### 2.3 Conditional Order Tests
|
|
```solidity
|
|
// File: contracts/tests/MycoConditionalOrder.test.ts
|
|
|
|
test("getTradeableOrder returns valid GPv2Order for buy", async () => {
|
|
const staticInput = abi.encode(
|
|
['tuple(uint8,uint256,uint256,address,uint256)'],
|
|
[[
|
|
0, // BUY direction
|
|
ethers.parseUnits("500", 6), // 500 USDC
|
|
ethers.parseUnits("900", 18), // 900 MYCO min
|
|
user.address, // receiver
|
|
86400 // 24 hour validity
|
|
]]
|
|
);
|
|
|
|
const offchainInput = abi.encode(
|
|
['tuple(uint256,uint32)'],
|
|
[[
|
|
ethers.parseUnits("1000", 18), // 1000 MYCO quote
|
|
Math.floor(Date.now() / 1000) + 3600 // expires in 1h
|
|
]]
|
|
);
|
|
|
|
const order = await handler.getTradeableOrder(
|
|
owner.address,
|
|
watchtower.address,
|
|
staticInput,
|
|
offchainInput
|
|
);
|
|
|
|
expect(order.sellToken).to.equal(usdc.address);
|
|
expect(order.buyToken).to.equal(myco.address);
|
|
expect(order.sellAmount).to.equal(ethers.parseUnits("500", 6));
|
|
expect(order.buyAmount).to.equal(ethers.parseUnits("1000", 18));
|
|
expect(order.receiver).to.equal(user.address);
|
|
});
|
|
|
|
test("getTradeableOrder reverts if quote expired", async () => {
|
|
const staticInput = abi.encode(
|
|
['tuple(uint8,uint256,uint256,address,uint256)'],
|
|
[[0, ethers.parseUnits("500", 6), ethers.parseUnits("900", 18), user.address, 86400]]
|
|
);
|
|
|
|
const expiredQuote = abi.encode(
|
|
['tuple(uint256,uint32)'],
|
|
[[ethers.parseUnits("1000", 18), Math.floor(Date.now() / 1000) - 1]] // Already expired
|
|
);
|
|
|
|
await expect(
|
|
handler.getTradeableOrder(owner.address, watchtower.address, staticInput, expiredQuote)
|
|
).to.be.revertedWithCustomError(handler, "OrderNotValid");
|
|
});
|
|
|
|
test("getTradeableOrder reverts if output below minimum", async () => {
|
|
const staticInput = abi.encode(
|
|
['tuple(uint8,uint256,uint256,address,uint256)'],
|
|
[[0, ethers.parseUnits("500", 6), ethers.parseUnits("2000", 18), user.address, 86400]] // Want 2000 MYCO min
|
|
);
|
|
|
|
const lowQuote = abi.encode(
|
|
['tuple(uint256,uint32)'],
|
|
[[ethers.parseUnits("1000", 18), Math.floor(Date.now() / 1000) + 3600]] // Only 1000 available
|
|
);
|
|
|
|
await expect(
|
|
handler.getTradeableOrder(owner.address, watchtower.address, staticInput, lowQuote)
|
|
).to.be.revertedWithCustomError(handler, "OrderNotValid");
|
|
});
|
|
|
|
test("verify function validates order", async () => {
|
|
const staticInput = abi.encode(['tuple(...)'], [/*...*/]);
|
|
const offchainInput = abi.encode(['tuple(...)'], [/*...*/]);
|
|
const order = await handler.getTradeableOrder(/*...*/);
|
|
|
|
// Should not revert
|
|
await handler.verify(
|
|
owner.address,
|
|
watchtower.address,
|
|
ethers.ZeroHash,
|
|
ethers.ZeroHash,
|
|
staticInput,
|
|
offchainInput,
|
|
order
|
|
);
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 3: Integration Testing (7-10 days)
|
|
|
|
### 3.1 Local Fork Testing
|
|
```bash
|
|
# Test against Base mainnet state
|
|
forge test --fork-url $BASE_RPC --fork-block-number $(cast block-number --rpc-url $BASE_RPC)
|
|
```
|
|
|
|
Key tests:
|
|
- [ ] Real GPv2Settlement at `0x9008D19f...`
|
|
- [ ] Real VaultRelayer interaction
|
|
- [ ] Real Balancer Vault integration
|
|
- [ ] Gas usage benchmarks
|
|
|
|
### 3.2 Mock Settlement Testing
|
|
- [ ] Deploy mock GPv2Settlement
|
|
- [ ] Test full settlement flow:
|
|
- [ ] User approves Balancer Vault
|
|
- [ ] Settlement pulls USDC via VaultRelayer
|
|
- [ ] Calls BondingCurveAdapter.executeBuyForCoW()
|
|
- [ ] Transfers output MYCO to user
|
|
- [ ] Verify all balances updated correctly
|
|
|
|
### 3.3 End-to-End Scenario Tests
|
|
|
|
**Scenario 1: Simple Buy**
|
|
```
|
|
1. User balance: 5000 USDC, 100 MYCO
|
|
2. User creates conditional order: Buy 1000 MYCO with 500 USDC
|
|
3. Watch Tower polls, gets quote: 1000 MYCO available
|
|
4. Solver submits settlement
|
|
5. Expected result:
|
|
- User: 4500 USDC, 1100 MYCO
|
|
- Curve reserve: +500 USDC
|
|
- Curve supply: -1000 MYCO
|
|
```
|
|
|
|
**Scenario 2: Multiple Orders in Batch**
|
|
```
|
|
1. Alice: Buy 1000 MYCO with 500 USDC
|
|
2. Bob: Buy 600 MYCO with 300 USDC
|
|
3. Both orders in same batch
|
|
4. Solver settles both atomically
|
|
5. Verify uniform clearing price applied
|
|
```
|
|
|
|
**Scenario 3: Slippage Protection**
|
|
```
|
|
1. User orders: Buy at least 1000 MYCO for 500 USDC
|
|
2. Quote drops to 900 MYCO between poll and execution
|
|
3. Watch Tower retries on next block
|
|
4. Quote recovers to 1200 MYCO
|
|
5. Order executes successfully
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 4: Watch Tower Integration (5-7 days)
|
|
|
|
### 4.1 Testnet Deployment (Base Sepolia)
|
|
- [ ] Deploy MycoBondingCurve on Sepolia
|
|
- [ ] Deploy MycoToken on Sepolia
|
|
- [ ] Deploy BondingCurveAdapter on Sepolia
|
|
- [ ] Deploy MycoConditionalOrder on Sepolia
|
|
- [ ] Register handler in test ComposableCoW
|
|
|
|
### 4.2 Request Watch Tower Access
|
|
- [ ] Contact CoW Protocol team
|
|
- [ ] Provide:
|
|
- [ ] Handler contract address
|
|
- [ ] Network (Sepolia)
|
|
- [ ] Expected order volume (orders/hour)
|
|
- [ ] Handler metadata (name, description, quote endpoints)
|
|
|
|
### 4.3 Monitor Watch Tower Polling
|
|
- [ ] Instrument handler to log polling calls
|
|
- [ ] Track:
|
|
- [ ] Number of polls per block
|
|
- [ ] Quote latency
|
|
- [ ] Error rates
|
|
- [ ] Poll frequency distribution
|
|
|
|
### 4.4 Test Real Order Submission
|
|
- [ ] Submit test conditional order via API
|
|
- [ ] Verify Watch Tower picks it up
|
|
- [ ] Monitor solver network reception
|
|
- [ ] Check execution in settlement
|
|
|
|
---
|
|
|
|
## Phase 5: Security Audit & Hardening (5-7 days)
|
|
|
|
### 5.1 Code Review Checklist
|
|
- [ ] **Arithmetic Safety**
|
|
- [ ] No overflow/underflow (using uint256 carefully)
|
|
- [ ] Price calculations avoid division before multiplication
|
|
- [ ] Fee calculation doesn't truncate incorrectly
|
|
|
|
- [ ] **Authorization**
|
|
- [ ] `onlySettlement` on execution functions ✓
|
|
- [ ] No backdoor mint/burn mechanisms
|
|
- [ ] Adapter can't be called directly by users
|
|
|
|
- [ ] **State Consistency**
|
|
- [ ] reserveBalance always accurate
|
|
- [ ] accumulatedFees tracked correctly
|
|
- [ ] totalSupply matches bonding curve state
|
|
|
|
- [ ] **Reentrancy**
|
|
- [ ] ReentrancyGuard on BondingCurveAdapter ✓
|
|
- [ ] ReentrancyGuard on executeBuyForCoW/executeSellForCoW ✓
|
|
- [ ] No external calls before state updates
|
|
|
|
- [ ] **Token Handling**
|
|
- [ ] SafeERC20 used everywhere ✓
|
|
- [ ] Approval amounts reasonable
|
|
- [ ] No unprotected transfer() calls
|
|
|
|
### 5.2 Fuzzing
|
|
```solidity
|
|
// Use Foundry fuzzing for property testing
|
|
|
|
function testFuzz_buyAndSellPreservesReserve(
|
|
uint256 buyAmount,
|
|
uint256 sellAmount
|
|
) public {
|
|
// Buy then sell should roughly preserve reserve (minus fees)
|
|
// Fuzz over various amounts and check invariant
|
|
}
|
|
|
|
function testFuzz_priceMonotonicity(uint256 supply1, uint256 supply2) public {
|
|
// Larger supply should have larger price
|
|
if (supply1 < supply2) {
|
|
assertTrue(curve.getPrice(supply1) <= curve.getPrice(supply2));
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5.3 Security Considerations
|
|
- [ ] Test malicious input:
|
|
- [ ] Zero amounts
|
|
- [ ] MAX_UINT256 amounts
|
|
- [ ] Mismatched decimals
|
|
- [ ] Expired quotes
|
|
- [ ] Invalid order kinds
|
|
|
|
- [ ] Test edge cases:
|
|
- [ ] First trade (supply = 0)
|
|
- [ ] Trade with no liquidity
|
|
- [ ] Very small trades (dust amounts)
|
|
- [ ] Very large trades (supply exhaustion)
|
|
|
|
- [ ] External security review recommended:
|
|
- [ ] Smart contract audit firm
|
|
- [ ] Focus: adapter integration with Settlement
|
|
|
|
---
|
|
|
|
## Phase 6: Mainnet Preparation (3-5 days)
|
|
|
|
### 6.1 Deployment Configuration
|
|
- [ ] Contract initialization parameters:
|
|
- [ ] Base price (in USDC, 6 decimals)
|
|
- [ ] Coefficient
|
|
- [ ] Exponent
|
|
- [ ] Fee percentage (basis points)
|
|
- [ ] Treasury address
|
|
|
|
- [ ] Token addresses on Base:
|
|
- [ ] USDC: `0x833589fC4D06F649c466dB920d0135aa6Df1cDEA`
|
|
- [ ] MYCO: (your token)
|
|
- [ ] Settlement: `0x9008D19f58AAbD9eD0D60971565AA8510560ab41`
|
|
|
|
### 6.2 Deployment Scripts
|
|
```bash
|
|
# Script: deploy-base.sh
|
|
|
|
# 1. Deploy MycoToken
|
|
npx hardhat run scripts/deployToken.ts --network base
|
|
|
|
# 2. Deploy MycoBondingCurve
|
|
npx hardhat run scripts/deployCurve.ts --network base
|
|
|
|
# 3. Deploy BondingCurveAdapter
|
|
npx hardhat run scripts/deployAdapter.ts --network base
|
|
|
|
# 4. Deploy MycoConditionalOrder
|
|
npx hardhat run scripts/deployHandler.ts --network base
|
|
|
|
# 5. Register in ComposableCoW (if needed)
|
|
npx hardhat run scripts/registerHandler.ts --network base
|
|
|
|
# 6. Verify contracts on Basescan
|
|
npx hardhat verify --network base <address> <constructor-args>
|
|
```
|
|
|
|
### 6.3 Pre-launch Checklist
|
|
- [ ] All contracts deployed and verified
|
|
- [ ] Handler registered with Watch Tower
|
|
- [ ] USDC/MYCO liquidity bootstrapped
|
|
- [ ] Initial price parameters approved by governance
|
|
- [ ] Treasury address set
|
|
- [ ] Comprehensive documentation published
|
|
- [ ] User guide created
|
|
- [ ] Community announcement planned
|
|
|
|
---
|
|
|
|
## Phase 7: Launch & Monitoring (Ongoing)
|
|
|
|
### 7.1 Launch Day
|
|
- [ ] Enable conditional orders at T=0
|
|
- [ ] Monitor:
|
|
- [ ] Order flow (orders/hour)
|
|
- [ ] Watch Tower polling health
|
|
- [ ] Quote latency
|
|
- [ ] Settlement success rate
|
|
- [ ] Gas usage
|
|
- [ ] Average execution price (vs. bonding curve)
|
|
|
|
### 7.2 Metrics to Track
|
|
```
|
|
Daily:
|
|
- Orders submitted / executed / failed
|
|
- Average slippage vs. quote
|
|
- MEV savings vs. alternative DEX
|
|
- Transaction costs
|
|
- User count
|
|
|
|
Weekly:
|
|
- Volume by order type
|
|
- Solver performance
|
|
- Watch Tower polling stats
|
|
- Price feed stability
|
|
|
|
Monthly:
|
|
- User retention
|
|
- Protocol revenue (accumulated fees)
|
|
- Governance parameter proposals
|
|
```
|
|
|
|
### 7.3 Incident Response
|
|
- [ ] Setup alerting for:
|
|
- [ ] Failed settlements
|
|
- [ ] Quote expiry rate >5%
|
|
- [ ] Unusual slippage
|
|
- [ ] Gas spikes
|
|
- [ ] Contract errors
|
|
|
|
- [ ] Emergency procedures:
|
|
- [ ] Pause conditional orders (via registry)
|
|
- [ ] Update fee parameters
|
|
- [ ] Upgrade handler logic (if proxy)
|
|
- [ ] Contact CoW Protocol team if systemic issue
|
|
|
|
---
|
|
|
|
## Testing Infrastructure
|
|
|
|
### Hardhat Setup
|
|
```typescript
|
|
// hardhat.config.ts
|
|
import "@nomicfoundation/hardhat-toolbox";
|
|
import "@openzeppelin/hardhat-upgrades";
|
|
|
|
export default {
|
|
networks: {
|
|
base: {
|
|
url: process.env.BASE_RPC_URL,
|
|
accounts: [process.env.PRIVATE_KEY],
|
|
chainId: 8453,
|
|
},
|
|
baseSepolia: {
|
|
url: process.env.BASE_SEPOLIA_RPC_URL,
|
|
accounts: [process.env.PRIVATE_KEY],
|
|
chainId: 84532,
|
|
},
|
|
},
|
|
solidity: {
|
|
version: "0.8.24",
|
|
settings: {
|
|
optimizer: {
|
|
enabled: true,
|
|
runs: 200,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
```
|
|
|
|
### Test Framework
|
|
```bash
|
|
# Run all tests
|
|
npm run test
|
|
|
|
# Run with coverage
|
|
npm run test:coverage
|
|
|
|
# Run fork tests
|
|
npm run test:fork:base
|
|
|
|
# Gas benchmarking
|
|
npm run test:gas
|
|
```
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
### Technical
|
|
- [ ] All tests passing (100% coverage for critical paths)
|
|
- [ ] Gas usage < 500k for settlement
|
|
- [ ] Quote latency < 100ms
|
|
- [ ] Watch Tower polling success rate > 99%
|
|
- [ ] Zero critical security findings in audit
|
|
|
|
### Operational
|
|
- [ ] 10+ orders settled successfully
|
|
- [ ] Average execution price within 0.1% of quote
|
|
- [ ] Zero failed settlements in first week
|
|
- [ ] User documentation complete
|
|
- [ ] Community feedback incorporated
|
|
|
|
### Business
|
|
- [ ] >$100k total volume settled
|
|
- [ ] >50 unique users
|
|
- [ ] >95% user satisfaction rating
|
|
- [ ] Community approval for mainnet launch
|
|
|
|
---
|
|
|
|
**Document Version:** 1.0
|
|
**Last Updated:** 2026-04-03
|
|
**Estimated Total Timeline:** 4-6 weeks from research → mainnet launch
|